<commit_before>/*
 * Copyright (C) 2020 ScyllaDB
 */

/*
 * This file is part of Scylla.
 *
 * Scylla is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Scylla 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Scylla.  If not, see <http://www.gnu.org/licenses/>.
 */
#pragma once

#include <seastar/core/condition-variable.hh>
#include "raft.hh"
#include "progress.hh"
#include "log.hh"

namespace raft {

// State of the FSM that needs logging & sending.
struct fsm_output {
    term_t term;
    server_id vote;
    std::vector<log_entry_ptr> log_entries;
    std::vector<std::pair<server_id, rpc_message>> messages;
    // Entries to apply.
    std::vector<log_entry_ptr> committed;
};

struct fsm_config {
    // max size of appended entries in bytes
    size_t append_request_threshold;
};

// 3.4 Leader election
// If a follower receives no communication over a period of
// time called the election timeout, then it assumes there is
// no viable leader and begins an election to choose a new
// leader.
static constexpr logical_clock::duration ELECTION_TIMEOUT = logical_clock::duration{10};

// 3.3 Raft Basics
// At any given time each server is in one of three states:
// leader, follower, or candidate.
// In normal operation there is exactly one leader and all of the
// other servers are followers. Followers are passive: they issue
// no requests on their own but simply respond to requests from
// leaders and candidates. The leader handles all client requests
// (if a client contacts a follower, the follower redirects it to
// the leader). The third state, candidate, is used to elect a new
// leader.
class follower {};
class candidate {};
class leader {};

// Raft protocol finite state machine
//
// Most libraries separate themselves from implementations by
// providing an API to the environment of the Raft protocol, such
// as the database, the write ahead log and the RPC to peers.

// This callback based design has some  drawbacks:

// - some callbacks may be defined in blocking model; e.g.
//  writing log entries to disk, or persisting the current
//  term in the database; Seastar has no blocking IO and
//  would have to emulate it with fibers;
// - the API calls are spread over the state machine
//  implementation, which makes reasoning about the correctness
//  more difficult (what happens if the library is is accessed
//  concurrently by multiple users, which of these accesses have
//  to be synchronized; what if the callback fails, is the state
//  machine handling the error correctly?)
// - while using callbacks allow testing without a real network or disk,
//  it still complicates it, since one has to implement meaningful
//  mocks for most of the APIs.
//
// Seastar Raft instead implements an instance of Raft as
// in-memory state machine with a catch-all API step(message)
// method. The method handles any kind of input and performs the
// needed state machine state transitions. To get state machine output
// poll_output() function has to be called. This call produces an output
// object, which encapsulates a list of actions that must be
// performed until the next poll_output() call can be made. The time is
// represented with a logical timer. The client is responsible for
// periodically invoking tick() method, which advances the state
// machine time and allows it to track such events as election or
// heartbeat timeouts.
class fsm {
    // id of this node
    server_id _my_id;
    // id of the current leader
    server_id _current_leader;
    // What state the server is in. The default is follower.
    std::variant<follower, candidate, leader> _state;
    // _current_term, _voted_for && _log are persisted in storage
    // The latest term the server has seen.
    term_t _current_term;
    // Candidate id that received a vote in the current term (or
    // nil if none).
    server_id _voted_for;
    // Index of the highest log entry known to be committed.
    // Currently not persisted.
    index_t _commit_idx = index_t(0);
    // Log entries; each entry contains a command for state machine,
    // and the term when the entry was received by the leader.
    log _log;
    // A possibly shared server failure detector.
    failure_detector& _failure_detector;
    // fsm configuration
    fsm_config _config;

    // Stores the last state observed by get_output().
    // Is updated with the actual state of the FSM after
    // fsm_output is created.
    struct last_observed_state {
        term_t _current_term;
        server_id _voted_for;
        index_t _commit_idx;

        bool is_equal(const fsm& fsm) const {
            return _current_term == fsm._current_term && _voted_for == fsm._voted_for &&
                _commit_idx == fsm._commit_idx;
        }

        void advance(const fsm& fsm) {
            _current_term = fsm._current_term;
            _voted_for = fsm._voted_for;
            _commit_idx = fsm._commit_idx;
        }
    } _observed;

    logical_clock _clock;
    // Start of the current election epoch - a time point relative
    // to which we expire election timeout.
    logical_clock::time_point _last_election_time = logical_clock::min();
    // A random value in range [election_timeout, 2 * election_timeout),
    // reset on each term change.
    logical_clock::duration _randomized_election_timeout = ELECTION_TIMEOUT;
    // Votes received during an election round. Available only in
    // candidate state.
    std::optional<votes> _votes;

    // A state for each follower, maintained only on the leader.
    std::optional<tracker> _tracker;
    // Holds all replies to AppendEntries RPC which are not
    // yet sent out. If AppendEntries request is accepted, we must
    // withhold a reply until the respective entry is persisted in
    // the log. Otherwise, e.g. when we receive AppendEntries with
    // an older term, we may reject it immediately.
    // Either way all replies are appended to this queue first.
    //
    // 3.3 Raft Basics
    // If a server receives a request with a stale term number, it
    // rejects the request.
    // TLA+ line 328
    std::vector<std::pair<server_id, rpc_message>> _messages;

    // Currently used configuration, may be different from
    // the committed during a configuration change.
    configuration _current_config;

    // Signaled when there is a IO event to process.
    seastar::condition_variable _sm_events;
    // Called when one of the replicas advances its match index
    // so it may be the case that some entries are committed now.
    // Signals _sm_events.
    void check_committed();
    // Check if the randomized election timeout has expired.
    bool is_past_election_timeout() const {
        return _clock.now() - _last_election_time >= _randomized_election_timeout;
    }
    // How much time has passed since last election or last
    // time we heard from a valid leader.
    logical_clock::duration election_elapsed() const {
        return _clock.now() - _last_election_time;
    }

    // A helper to send any kind of RPC message.
    template <typename Message>
    void send_to(server_id to, Message&& m) {
        static_assert(std::is_rvalue_reference<decltype(m)>::value, "must be rvalue");
        _messages.push_back(std::make_pair(to, std::move(m)));
        _sm_events.signal();
    }

    // A helper to update the FSM's current term.
    void update_current_term(term_t current_term);

    void check_is_leader() const {
        if (!is_leader()) {
            throw not_a_leader(_current_leader);
        }
    }

    void become_candidate();

    void become_follower(server_id leader);

    // Controls whether the follower has been responsive recently,
    // so it makes sense to send more data to it.
    bool can_send_to(const follower_progress& progress);
    // Replicate entries to a follower. If there are no entries to send
    // and allow_empty is true, send a heartbeat.
    void replicate_to(follower_progress& progress, bool allow_empty);
    void replicate();
    void append_entries(server_id from, append_request_recv&& append_request);
    void append_entries_reply(server_id from, append_reply&& reply);

    void request_vote(server_id from, vote_request&& vote_request);
    void request_vote_reply(server_id from, vote_reply&& vote_reply);

    // Called on a follower with a new known leader commit index.
    // Advances the follower's commit index up to all log-stable
    // entries, known to be committed.
    void advance_commit_idx(index_t leader_commit_idx);
    // Called after log entries in FSM output are considered persisted.
    // Produces new FSM output.
    void advance_stable_idx(index_t idx);
    // Tick implementation on a leader
    void tick_leader();

    // Set cluster configuration
    void set_configuration(const configuration& config) {
        _current_config = config;
        // We unconditionally access _current_config
        // to identify which entries are committed.
        assert(_current_config.servers.size() > 0);
        if (is_leader()) {
            _tracker->set_configuration(_current_config.servers, _log.next_idx());
        } else if (is_candidate()) {
            _votes->set_configuration(_current_config.servers);
        }
    }
public:
    explicit fsm(server_id id, term_t current_term, server_id voted_for, log log,
            failure_detector& failure_detector, fsm_config conf);

    bool is_leader() const {
        return std::holds_alternative<leader>(_state);
    }
    bool is_follower() const {
        return std::holds_alternative<follower>(_state);
    }
    bool is_candidate() const {
        return std::holds_alternative<candidate>(_state);
    }

    void become_leader();

    // Add an entry to in-memory log. The entry has to be
    // committed to the persistent Raft log afterwards.
    template<typename T> const log_entry& add_entry(T command);

    // Wait until there is, and return state machine output that
    // needs to be handled.
    // This includes a list of the entries that need
    // to be logged. The logged entries are eventually
    // discarded from the state machine after snapshotting.
    future<fsm_output> poll_output();

    // Get state machine output, if there is any. Doesn't
    // wait. It is public for use in testing.
    // May throw on allocation failure, but leaves state machine
    // in the same state in that case
    fsm_output get_output();

    // Called to advance virtual clock of the protocol state machine.
    void tick();

    // Feed one Raft RPC message into the state machine.
    // Advances the state machine state and generates output,
    // accessible via poll_output().
    template <typename Message>
    void step(server_id from, Message&& msg);

    void stop();

    // @sa can_read()
    term_t get_current_term() const {
        return _current_term;
    }

    // Should be called on leader only, throws otherwise.
    // Returns true if the current leader has at least one entry
    // committed and a quorum of followers was alive in the last
    // tick period.
    bool can_read();

    friend std::ostream& operator<<(std::ostream& os, const fsm& f);
};

template <typename Message>
void fsm::step(server_id from, Message&& msg) {
    static_assert(std::is_rvalue_reference<decltype(msg)>::value, "must be rvalue");
    // 4.1. Safety
    // Servers process incoming RPC requests without consulting
    // their current configurations.

    // 3.3. Raft basics.
    //
    // Current terms are exchanged whenever servers
    // communicate; if one server’s current term is smaller
    // than the other’s, then it updates its current term to
    // the larger value. If a candidate or leader discovers
    // that its term is out of date, it immediately reverts to
    // follower state. If a server receives a request with
    // a stale term number, it rejects the request.
    if (msg.current_term > _current_term) {
        logger.trace("{} [term: {}] received a message with higher term from {} [term: {}]",
            _my_id, _current_term, from, msg.current_term);

        if constexpr (std::is_same_v<Message, append_request_recv>) {
            become_follower(from);
        } else {
            if constexpr (std::is_same_v<Message, vote_request>) {
                if (_current_leader != server_id{} && election_elapsed() < ELECTION_TIMEOUT) {
                    // 4.2.3 Disruptive servers
                    // If a server receives a RequestVote request
                    // within the minimum election timeout of
                    // hearing from a current leader, it does not
                    // update its term or grant its vote.
                    logger.trace("{} [term: {}] not granting a vote within a minimum election timeout, elapsed {}",
                        _my_id, _current_term, election_elapsed());
                    return;
                }
            }
            become_follower(server_id{});
        }
        update_current_term(msg.current_term);

    } else if (msg.current_term < _current_term) {
        if constexpr (std::is_same_v<Message, append_request_recv>) {
            // Instructs the leader to step down.
            append_reply reply{_current_term, _commit_idx, append_reply::rejected{msg.prev_log_idx, _log.last_idx()}};
            send_to(from, std::move(reply));
        } else {
            // Ignore other cases
            logger.trace("{} [term: {}] ignored a message with lower term from {} [term: {}]",
                _my_id, _current_term, from, msg.current_term);
        }
        return;

    } else /* _current_term == msg.current_term */ {
        if constexpr (std::is_same_v<Message, append_request_recv> ||
                      std::is_same_v<Message, install_snapshot>) {
            if (is_candidate()) {
                // 3.4 Leader Election
                // While waiting for votes, a candidate may receive an AppendEntries
                // RPC from another server claiming to be leader. If the
                // leader’s term (included in its RPC) is at least as large as the
                // candidate’s current term, then the candidate recognizes the
                // leader as legitimate and returns to follower state.
                become_follower(from);
            } else if (_current_leader == server_id{}) {
                // Earlier we changed our term to match a candidate's
                // term. Now we get the first message from the
                // newly elected leader. Keep track of the current
                // leader to avoid starting an election if the
                // leader becomes idle.
                _current_leader = from;
            }
            assert(_current_leader == from);
        }
    }

    auto visitor = [this, from, msg = std::move(msg)](auto state) mutable {
        using State = decltype(state);

        if constexpr (std::is_same_v<Message, append_request_recv>) {
            // Got AppendEntries RPC from self
            append_entries(from, std::move(msg));
        } else if constexpr (std::is_same_v<Message, append_reply>) {
            if constexpr (!std::is_same_v<State, leader>) {
                // Ignore stray reply if we're not a leader.
                return;
            }
            append_entries_reply(from, std::move(msg));
        } else if constexpr (std::is_same_v<Message, vote_request>) {
            request_vote(from, std::move(msg));
        } else if constexpr (std::is_same_v<Message, vote_reply>) {
            if constexpr (!std::is_same_v<State, candidate>) {
                // Ignore stray reply if we're not a candidate.
                return;
            }
            request_vote_reply(from, std::move(msg));
        }
    };

    std::visit(visitor, _state);
}

} // namespace raft

<commit_msg>raft: make election_elapsed public for testing<commit_after>/*
 * Copyright (C) 2020 ScyllaDB
 */

/*
 * This file is part of Scylla.
 *
 * Scylla is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Scylla 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Scylla.  If not, see <http://www.gnu.org/licenses/>.
 */
#pragma once

#include <seastar/core/condition-variable.hh>
#include "raft.hh"
#include "progress.hh"
#include "log.hh"

namespace raft {

// State of the FSM that needs logging & sending.
struct fsm_output {
    term_t term;
    server_id vote;
    std::vector<log_entry_ptr> log_entries;
    std::vector<std::pair<server_id, rpc_message>> messages;
    // Entries to apply.
    std::vector<log_entry_ptr> committed;
};

struct fsm_config {
    // max size of appended entries in bytes
    size_t append_request_threshold;
};

// 3.4 Leader election
// If a follower receives no communication over a period of
// time called the election timeout, then it assumes there is
// no viable leader and begins an election to choose a new
// leader.
static constexpr logical_clock::duration ELECTION_TIMEOUT = logical_clock::duration{10};

// 3.3 Raft Basics
// At any given time each server is in one of three states:
// leader, follower, or candidate.
// In normal operation there is exactly one leader and all of the
// other servers are followers. Followers are passive: they issue
// no requests on their own but simply respond to requests from
// leaders and candidates. The leader handles all client requests
// (if a client contacts a follower, the follower redirects it to
// the leader). The third state, candidate, is used to elect a new
// leader.
class follower {};
class candidate {};
class leader {};

// Raft protocol finite state machine
//
// Most libraries separate themselves from implementations by
// providing an API to the environment of the Raft protocol, such
// as the database, the write ahead log and the RPC to peers.

// This callback based design has some  drawbacks:

// - some callbacks may be defined in blocking model; e.g.
//  writing log entries to disk, or persisting the current
//  term in the database; Seastar has no blocking IO and
//  would have to emulate it with fibers;
// - the API calls are spread over the state machine
//  implementation, which makes reasoning about the correctness
//  more difficult (what happens if the library is is accessed
//  concurrently by multiple users, which of these accesses have
//  to be synchronized; what if the callback fails, is the state
//  machine handling the error correctly?)
// - while using callbacks allow testing without a real network or disk,
//  it still complicates it, since one has to implement meaningful
//  mocks for most of the APIs.
//
// Seastar Raft instead implements an instance of Raft as
// in-memory state machine with a catch-all API step(message)
// method. The method handles any kind of input and performs the
// needed state machine state transitions. To get state machine output
// poll_output() function has to be called. This call produces an output
// object, which encapsulates a list of actions that must be
// performed until the next poll_output() call can be made. The time is
// represented with a logical timer. The client is responsible for
// periodically invoking tick() method, which advances the state
// machine time and allows it to track such events as election or
// heartbeat timeouts.
class fsm {
    // id of this node
    server_id _my_id;
    // id of the current leader
    server_id _current_leader;
    // What state the server is in. The default is follower.
    std::variant<follower, candidate, leader> _state;
    // _current_term, _voted_for && _log are persisted in storage
    // The latest term the server has seen.
    term_t _current_term;
    // Candidate id that received a vote in the current term (or
    // nil if none).
    server_id _voted_for;
    // Index of the highest log entry known to be committed.
    // Currently not persisted.
    index_t _commit_idx = index_t(0);
    // Log entries; each entry contains a command for state machine,
    // and the term when the entry was received by the leader.
    log _log;
    // A possibly shared server failure detector.
    failure_detector& _failure_detector;
    // fsm configuration
    fsm_config _config;

    // Stores the last state observed by get_output().
    // Is updated with the actual state of the FSM after
    // fsm_output is created.
    struct last_observed_state {
        term_t _current_term;
        server_id _voted_for;
        index_t _commit_idx;

        bool is_equal(const fsm& fsm) const {
            return _current_term == fsm._current_term && _voted_for == fsm._voted_for &&
                _commit_idx == fsm._commit_idx;
        }

        void advance(const fsm& fsm) {
            _current_term = fsm._current_term;
            _voted_for = fsm._voted_for;
            _commit_idx = fsm._commit_idx;
        }
    } _observed;

    logical_clock _clock;
    // Start of the current election epoch - a time point relative
    // to which we expire election timeout.
    logical_clock::time_point _last_election_time = logical_clock::min();
    // A random value in range [election_timeout, 2 * election_timeout),
    // reset on each term change.
    logical_clock::duration _randomized_election_timeout = ELECTION_TIMEOUT;
    // Votes received during an election round. Available only in
    // candidate state.
    std::optional<votes> _votes;

    // A state for each follower, maintained only on the leader.
    std::optional<tracker> _tracker;
    // Holds all replies to AppendEntries RPC which are not
    // yet sent out. If AppendEntries request is accepted, we must
    // withhold a reply until the respective entry is persisted in
    // the log. Otherwise, e.g. when we receive AppendEntries with
    // an older term, we may reject it immediately.
    // Either way all replies are appended to this queue first.
    //
    // 3.3 Raft Basics
    // If a server receives a request with a stale term number, it
    // rejects the request.
    // TLA+ line 328
    std::vector<std::pair<server_id, rpc_message>> _messages;

    // Currently used configuration, may be different from
    // the committed during a configuration change.
    configuration _current_config;

    // Signaled when there is a IO event to process.
    seastar::condition_variable _sm_events;
    // Called when one of the replicas advances its match index
    // so it may be the case that some entries are committed now.
    // Signals _sm_events.
    void check_committed();
    // Check if the randomized election timeout has expired.
    bool is_past_election_timeout() const {
        return _clock.now() - _last_election_time >= _randomized_election_timeout;
    }

    // A helper to send any kind of RPC message.
    template <typename Message>
    void send_to(server_id to, Message&& m) {
        static_assert(std::is_rvalue_reference<decltype(m)>::value, "must be rvalue");
        _messages.push_back(std::make_pair(to, std::move(m)));
        _sm_events.signal();
    }

    // A helper to update the FSM's current term.
    void update_current_term(term_t current_term);

    void check_is_leader() const {
        if (!is_leader()) {
            throw not_a_leader(_current_leader);
        }
    }

    void become_candidate();

    void become_follower(server_id leader);

    // Controls whether the follower has been responsive recently,
    // so it makes sense to send more data to it.
    bool can_send_to(const follower_progress& progress);
    // Replicate entries to a follower. If there are no entries to send
    // and allow_empty is true, send a heartbeat.
    void replicate_to(follower_progress& progress, bool allow_empty);
    void replicate();
    void append_entries(server_id from, append_request_recv&& append_request);
    void append_entries_reply(server_id from, append_reply&& reply);

    void request_vote(server_id from, vote_request&& vote_request);
    void request_vote_reply(server_id from, vote_reply&& vote_reply);

    // Called on a follower with a new known leader commit index.
    // Advances the follower's commit index up to all log-stable
    // entries, known to be committed.
    void advance_commit_idx(index_t leader_commit_idx);
    // Called after log entries in FSM output are considered persisted.
    // Produces new FSM output.
    void advance_stable_idx(index_t idx);
    // Tick implementation on a leader
    void tick_leader();

    // Set cluster configuration
    void set_configuration(const configuration& config) {
        _current_config = config;
        // We unconditionally access _current_config
        // to identify which entries are committed.
        assert(_current_config.servers.size() > 0);
        if (is_leader()) {
            _tracker->set_configuration(_current_config.servers, _log.next_idx());
        } else if (is_candidate()) {
            _votes->set_configuration(_current_config.servers);
        }
    }
public:
    explicit fsm(server_id id, term_t current_term, server_id voted_for, log log,
            failure_detector& failure_detector, fsm_config conf);

    bool is_leader() const {
        return std::holds_alternative<leader>(_state);
    }
    bool is_follower() const {
        return std::holds_alternative<follower>(_state);
    }
    bool is_candidate() const {
        return std::holds_alternative<candidate>(_state);
    }

    void become_leader();

    // Add an entry to in-memory log. The entry has to be
    // committed to the persistent Raft log afterwards.
    template<typename T> const log_entry& add_entry(T command);

    // Wait until there is, and return state machine output that
    // needs to be handled.
    // This includes a list of the entries that need
    // to be logged. The logged entries are eventually
    // discarded from the state machine after snapshotting.
    future<fsm_output> poll_output();

    // Get state machine output, if there is any. Doesn't
    // wait. It is public for use in testing.
    // May throw on allocation failure, but leaves state machine
    // in the same state in that case
    fsm_output get_output();

    // Called to advance virtual clock of the protocol state machine.
    void tick();

    // Feed one Raft RPC message into the state machine.
    // Advances the state machine state and generates output,
    // accessible via poll_output().
    template <typename Message>
    void step(server_id from, Message&& msg);

    void stop();

    // @sa can_read()
    term_t get_current_term() const {
        return _current_term;
    }

    // How much time has passed since last election or last
    // time we heard from a valid leader.
    logical_clock::duration election_elapsed() const {
        return _clock.now() - _last_election_time;
    }

    // Should be called on leader only, throws otherwise.
    // Returns true if the current leader has at least one entry
    // committed and a quorum of followers was alive in the last
    // tick period.
    bool can_read();

    friend std::ostream& operator<<(std::ostream& os, const fsm& f);
};

template <typename Message>
void fsm::step(server_id from, Message&& msg) {
    static_assert(std::is_rvalue_reference<decltype(msg)>::value, "must be rvalue");
    // 4.1. Safety
    // Servers process incoming RPC requests without consulting
    // their current configurations.

    // 3.3. Raft basics.
    //
    // Current terms are exchanged whenever servers
    // communicate; if one server’s current term is smaller
    // than the other’s, then it updates its current term to
    // the larger value. If a candidate or leader discovers
    // that its term is out of date, it immediately reverts to
    // follower state. If a server receives a request with
    // a stale term number, it rejects the request.
    if (msg.current_term > _current_term) {
        logger.trace("{} [term: {}] received a message with higher term from {} [term: {}]",
            _my_id, _current_term, from, msg.current_term);

        if constexpr (std::is_same_v<Message, append_request_recv>) {
            become_follower(from);
        } else {
            if constexpr (std::is_same_v<Message, vote_request>) {
                if (_current_leader != server_id{} && election_elapsed() < ELECTION_TIMEOUT) {
                    // 4.2.3 Disruptive servers
                    // If a server receives a RequestVote request
                    // within the minimum election timeout of
                    // hearing from a current leader, it does not
                    // update its term or grant its vote.
                    logger.trace("{} [term: {}] not granting a vote within a minimum election timeout, elapsed {}",
                        _my_id, _current_term, election_elapsed());
                    return;
                }
            }
            become_follower(server_id{});
        }
        update_current_term(msg.current_term);

    } else if (msg.current_term < _current_term) {
        if constexpr (std::is_same_v<Message, append_request_recv>) {
            // Instructs the leader to step down.
            append_reply reply{_current_term, _commit_idx, append_reply::rejected{msg.prev_log_idx, _log.last_idx()}};
            send_to(from, std::move(reply));
        } else {
            // Ignore other cases
            logger.trace("{} [term: {}] ignored a message with lower term from {} [term: {}]",
                _my_id, _current_term, from, msg.current_term);
        }
        return;

    } else /* _current_term == msg.current_term */ {
        if constexpr (std::is_same_v<Message, append_request_recv> ||
                      std::is_same_v<Message, install_snapshot>) {
            if (is_candidate()) {
                // 3.4 Leader Election
                // While waiting for votes, a candidate may receive an AppendEntries
                // RPC from another server claiming to be leader. If the
                // leader’s term (included in its RPC) is at least as large as the
                // candidate’s current term, then the candidate recognizes the
                // leader as legitimate and returns to follower state.
                become_follower(from);
            } else if (_current_leader == server_id{}) {
                // Earlier we changed our term to match a candidate's
                // term. Now we get the first message from the
                // newly elected leader. Keep track of the current
                // leader to avoid starting an election if the
                // leader becomes idle.
                _current_leader = from;
            }
            assert(_current_leader == from);
        }
    }

    auto visitor = [this, from, msg = std::move(msg)](auto state) mutable {
        using State = decltype(state);

        if constexpr (std::is_same_v<Message, append_request_recv>) {
            // Got AppendEntries RPC from self
            append_entries(from, std::move(msg));
        } else if constexpr (std::is_same_v<Message, append_reply>) {
            if constexpr (!std::is_same_v<State, leader>) {
                // Ignore stray reply if we're not a leader.
                return;
            }
            append_entries_reply(from, std::move(msg));
        } else if constexpr (std::is_same_v<Message, vote_request>) {
            request_vote(from, std::move(msg));
        } else if constexpr (std::is_same_v<Message, vote_reply>) {
            if constexpr (!std::is_same_v<State, candidate>) {
                // Ignore stray reply if we're not a candidate.
                return;
            }
            request_vote_reply(from, std::move(msg));
        }
    };

    std::visit(visitor, _state);
}

} // namespace raft

<|endoftext|>


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

//$Id$

#ifndef FEATURE_STYLE_PROCESSOR_HPP
#define FEATURE_STYLE_PROCESSOR_HPP

// mapnik
#include <mapnik/box2d.hpp>
#include <mapnik/datasource.hpp>
#include <mapnik/layer.hpp>
#include <mapnik/map.hpp>
#include <mapnik/attribute_collector.hpp>
#include <mapnik/expression_evaluator.hpp>
#include <mapnik/utils.hpp>
#include <mapnik/projection.hpp>
#include <mapnik/scale_denominator.hpp>
#include <mapnik/memory_datasource.hpp>

#ifdef MAPNIK_DEBUG
//#include <mapnik/wall_clock_timer.hpp>
#endif
//stl
#include <vector>

namespace mapnik
{
template <typename Processor>
class feature_style_processor
{
    /** Calls the renderer's process function,
      * \param output     Renderer
      * \param f          Feature to process
      * \param prj_trans  Projection
      * \param sym        Symbolizer object
      */
    struct symbol_dispatch : public boost::static_visitor<>
    {
        symbol_dispatch (Processor & output,
                         Feature const& f,
                         proj_transform const& prj_trans)
            : output_(output),
              f_(f),
              prj_trans_(prj_trans)  {}

        template <typename T>
        void operator () (T const& sym) const
        {
            output_.process(sym,f_,prj_trans_);
        }

        Processor & output_;
        Feature const& f_;
        proj_transform const& prj_trans_;
    };
public:
    explicit feature_style_processor(Map const& m, double scale_factor = 1.0)
        : m_(m),
          scale_factor_(scale_factor) {}

    void apply()
    {
#ifdef MAPNIK_DEBUG
        //mapnik::wall_clock_progress_timer t(std::clog, "map rendering took: ");
#endif
        Processor & p = static_cast<Processor&>(*this);
        p.start_map_processing(m_);
        Map::const_metawriter_iterator metaItr = m_.begin_metawriters();
        Map::const_metawriter_iterator metaItrEnd = m_.end_metawriters();

        for (;metaItr!=metaItrEnd; ++metaItr)
        {
            metaItr->second->start();
        }

        try
        {
            projection proj(m_.srs()); // map projection
            double scale_denom = mapnik::scale_denominator(m_,proj.is_geographic());
            scale_denom *= scale_factor_;
#ifdef MAPNIK_DEBUG
            std::clog << "scale denominator = " << scale_denom << "\n";
#endif
            std::vector<layer>::const_iterator itr = m_.layers().begin();
            std::vector<layer>::const_iterator end = m_.layers().end();

            while (itr != end)
            {
                if (itr->isVisible(scale_denom))
                {
                    apply_to_layer(*itr, p, proj, scale_denom);
                }
                ++itr;
            }
        }
        catch (proj_init_error& ex)
        {
            std::clog << "proj_init_error:" << ex.what() << "\n";
        }

        metaItr = m_.begin_metawriters();
        for (;metaItr!=metaItrEnd; ++metaItr)
        {
            metaItr->second->stop();
        }

        p.end_map_processing(m_);
    }
private:
    void apply_to_layer(layer const& lay, Processor & p,
                        projection const& proj0, double scale_denom)
    {
#ifdef MAPNIK_DEBUG
        //wall_clock_progress_timer timer(clog, "end layer rendering: ");
#endif
        boost::shared_ptr<datasource> ds = lay.datasource();
        if (!ds) {
            std::clog << "WARNING: No datasource for layer '" << lay.name() << "'\n";
            return;
        }
        p.start_layer_processing(lay);
        if (ds)
        {
            box2d<double> ext = m_.get_buffered_extent();
            projection proj1(lay.srs());
            proj_transform prj_trans(proj0,proj1);

            box2d<double> layer_ext = lay.envelope();

            double lx0 = layer_ext.minx();
            double ly0 = layer_ext.miny();
            double lz0 = 0.0;
            double lx1 = layer_ext.maxx();
            double ly1 = layer_ext.maxy();
            double lz1 = 0.0;
            // back project layers extent into main map projection
            prj_trans.backward(lx0,ly0,lz0);
            prj_trans.backward(lx1,ly1,lz1);

            // if no intersection then nothing to do for layer
            if ( lx0 > ext.maxx() || lx1 < ext.minx() || ly0 > ext.maxy() || ly1 < ext.miny() )
            {
                return;
            }

            // clip query bbox
            lx0 = std::max(ext.minx(),lx0);
            ly0 = std::max(ext.miny(),ly0);
            lx1 = std::min(ext.maxx(),lx1);
            ly1 = std::min(ext.maxy(),ly1);

            prj_trans.forward(lx0,ly0,lz0);
            prj_trans.forward(lx1,ly1,lz1);
            box2d<double> bbox(lx0,ly0,lx1,ly1);

            query::resolution_type res(m_.width()/m_.get_current_extent().width(),m_.height()/m_.get_current_extent().height());
            query q(bbox,res,scale_denom); //BBOX query

            std::vector<std::string> const& style_names = lay.styles();
            std::vector<std::string>::const_iterator stylesIter = style_names.begin();
            std::vector<std::string>::const_iterator stylesEnd = style_names.end();
            memory_datasource cache;
            bool cache_features = style_names.size()>1?true:false;
            bool first = true;
            for (;stylesIter != stylesEnd; ++stylesIter)
            {
                std::set<std::string> names;
                attribute_collector collector(names);
                std::vector<rule_type*> if_rules;
                std::vector<rule_type*> else_rules;

                bool active_rules=false;

                boost::optional<feature_type_style const&> style=m_.find_style(*stylesIter);
                if (!style) {
                    std::clog << "WARNING: style '" << *stylesIter << "' required for layer '" << lay.name() << "' does not exist.\n";
                    continue;
                }

                const std::vector<rule_type>& rules=(*style).get_rules();
                std::vector<rule_type>::const_iterator ruleIter=rules.begin();
                std::vector<rule_type>::const_iterator ruleEnd=rules.end();

                for (;ruleIter!=ruleEnd;++ruleIter)
                {
                    if (ruleIter->active(scale_denom))
                    {
                        active_rules=true;
                        // collect unique attribute names
                        // TODO - in the future rasters should be able to be filtered...
                        if (ds->type() == datasource::Vector)
                        {
                            collector(*ruleIter);
                        }
                        if (ruleIter->has_else_filter())
                        {
                            else_rules.push_back(const_cast<rule_type*>(&(*ruleIter)));
                        }
                        else
                        {
                            if_rules.push_back(const_cast<rule_type*>(&(*ruleIter)));
                        }
                        if (ds->type() == datasource::Raster)
                        {
                            if (ds->params().get<double>("filter_factor",0.0) == 0.0)
                            {
                                const rule_type::symbolizers& symbols = ruleIter->get_symbolizers();
                                rule_type::symbolizers::const_iterator symIter = symbols.begin();
                                rule_type::symbolizers::const_iterator symEnd = symbols.end();
                                for (;symIter != symEnd;++symIter)
                                {
                                    try
                                    {
                                        raster_symbolizer sym = boost::get<raster_symbolizer>(*symIter);
                                        std::string scaling = sym.get_scaling();
                                        if (scaling == "bilinear" || scaling == "bilinear8" )
                                        {
                                            // todo - allow setting custom value in symbolizer property?
                                            q.filter_factor(2.0);
                                        }
                                    }
                                    catch (const boost::bad_get &v)
                                    {
                                        // case where useless symbolizer is attached to raster layer
                                        //throw config_error("Invalid Symbolizer type supplied, only RasterSymbolizer is supported");
                                    }
                                }
                            }

                        }
                    }
                }
                std::set<std::string>::const_iterator namesIter=names.begin();
                std::set<std::string>::const_iterator namesEnd =names.end();

                // push all property names
                for (;namesIter!=namesEnd;++namesIter)
                {
                    q.add_property_name(*namesIter);
                }
                if (active_rules)
                {
                    featureset_ptr fs;
                    if (first)
                    {
                        first = false;
                        fs = ds->features(q);
                    }
                    else
                    {
                        fs = cache.features(q);
                    }
                    if (fs)
                    {
                        feature_ptr feature;
                        while ((feature = fs->next()))
                        {
                            bool do_else=true;

                            if (cache_features)
                            {
                                cache.push(feature);
                            }

                            std::vector<rule_type*>::const_iterator itr=if_rules.begin();
                            std::vector<rule_type*>::const_iterator end=if_rules.end();
                            for (;itr != end;++itr)
                            {
                                expression_ptr const& expr=(*itr)->get_filter();
                                value_type result = boost::apply_visitor(evaluate<Feature,value_type>(*feature),*expr);
                                if (result.to_bool())
                                {
                                    do_else=false;
                                    const rule_type::symbolizers& symbols = (*itr)->get_symbolizers();
                                    rule_type::symbolizers::const_iterator symIter=symbols.begin();
                                    rule_type::symbolizers::const_iterator symEnd =symbols.end();
                                    for (;symIter != symEnd;++symIter)
                                    {
                                        boost::apply_visitor
                                            (symbol_dispatch(p,*feature,prj_trans),*symIter);
                                    }
                                }
                            }
                            if (do_else)
                            {
                                //else filter
                                std::vector<rule_type*>::const_iterator itr=
                                    else_rules.begin();
                                std::vector<rule_type*>::const_iterator end=
                                    else_rules.end();
                                for (;itr != end;++itr)
                                {
                                    const rule_type::symbolizers& symbols = (*itr)->get_symbolizers();
                                    rule_type::symbolizers::const_iterator symIter= symbols.begin();
                                    rule_type::symbolizers::const_iterator symEnd = symbols.end();

                                    for (;symIter!=symEnd;++symIter)
                                    {
                                        boost::apply_visitor
                                            (symbol_dispatch(p,*feature,prj_trans),*symIter);
                                    }
                                }
                            }
                        }
                        cache_features = false;
                    }
                }
            }
        }
        p.end_layer_processing(lay);
    }
    Map const& m_;
    double scale_factor_;
};
}

#endif //FEATURE_STYLE_PROCESSOR_HPP
<commit_msg>+ fix feature caching implementation - collect   attributes names from all active styles<commit_after>/*****************************************************************************
 *
 * This file is part of Mapnik (c++ mapping toolkit)
 *
 * Copyright (C) 2006 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
 *
 *****************************************************************************/

//$Id$

#ifndef FEATURE_STYLE_PROCESSOR_HPP
#define FEATURE_STYLE_PROCESSOR_HPP

// mapnik
#include <mapnik/box2d.hpp>
#include <mapnik/datasource.hpp>
#include <mapnik/layer.hpp>
#include <mapnik/map.hpp>
#include <mapnik/attribute_collector.hpp>
#include <mapnik/expression_evaluator.hpp>
#include <mapnik/utils.hpp>
#include <mapnik/projection.hpp>
#include <mapnik/scale_denominator.hpp>
#include <mapnik/memory_datasource.hpp>

#ifdef MAPNIK_DEBUG
//#include <mapnik/wall_clock_timer.hpp>
#endif
// boost
#include <boost/foreach.hpp>
//stl
#include <vector>

namespace mapnik
{

template <typename Processor>
class feature_style_processor
{
    /** Calls the renderer's process function,
      * \param output     Renderer
      * \param f          Feature to process
      * \param prj_trans  Projection
      * \param sym        Symbolizer object
      */
    struct symbol_dispatch : public boost::static_visitor<>
    {
        symbol_dispatch (Processor & output,
                         Feature const& f,
                         proj_transform const& prj_trans)
            : output_(output),
              f_(f),
              prj_trans_(prj_trans)  {}

        template <typename T>
        void operator () (T const& sym) const
        {
            output_.process(sym,f_,prj_trans_);
        }

        Processor & output_;
        Feature const& f_;
        proj_transform const& prj_trans_;
    };
public:
    explicit feature_style_processor(Map const& m, double scale_factor = 1.0)
        : m_(m),
          scale_factor_(scale_factor) {}

    void apply()
    {
#ifdef MAPNIK_DEBUG
        //mapnik::wall_clock_progress_timer t(std::clog, "map rendering took: ");
#endif
        Processor & p = static_cast<Processor&>(*this);
        p.start_map_processing(m_);
        Map::const_metawriter_iterator metaItr = m_.begin_metawriters();
        Map::const_metawriter_iterator metaItrEnd = m_.end_metawriters();

        for (;metaItr!=metaItrEnd; ++metaItr)
        {
            metaItr->second->start();
        }

        try
        {
            projection proj(m_.srs()); // map projection
            double scale_denom = mapnik::scale_denominator(m_,proj.is_geographic());
            scale_denom *= scale_factor_;
#ifdef MAPNIK_DEBUG
            std::clog << "scale denominator = " << scale_denom << "\n";
#endif
            BOOST_FOREACH ( layer const& lyr, m_.layers() )
            {
                if (lyr.isVisible(scale_denom))
                {
                    apply_to_layer(lyr, p, proj, scale_denom);
                }
            }
        }
        catch (proj_init_error& ex)
        {
            std::clog << "proj_init_error:" << ex.what() << "\n";
        }

        metaItr = m_.begin_metawriters();
        for (;metaItr!=metaItrEnd; ++metaItr)
        {
            metaItr->second->stop();
        }

        p.end_map_processing(m_);
    }
private:
    void apply_to_layer(layer const& lay, Processor & p,
                        projection const& proj0, double scale_denom)
    {
#ifdef MAPNIK_DEBUG
        //wall_clock_progress_timer timer(clog, "end layer rendering: ");
#endif
        boost::shared_ptr<datasource> ds = lay.datasource();
        if (!ds) {
            std::clog << "WARNING: No datasource for layer '" << lay.name() << "'\n";
            return;
        }
        p.start_layer_processing(lay);
        if (ds)
        {
            box2d<double> ext = m_.get_buffered_extent();
            projection proj1(lay.srs());
            proj_transform prj_trans(proj0,proj1);

            box2d<double> layer_ext = lay.envelope();

            double lx0 = layer_ext.minx();
            double ly0 = layer_ext.miny();
            double lz0 = 0.0;
            double lx1 = layer_ext.maxx();
            double ly1 = layer_ext.maxy();
            double lz1 = 0.0;
            // back project layers extent into main map projection
            prj_trans.backward(lx0,ly0,lz0);
            prj_trans.backward(lx1,ly1,lz1);

            // if no intersection then nothing to do for layer
            if ( lx0 > ext.maxx() || lx1 < ext.minx() || ly0 > ext.maxy() || ly1 < ext.miny() )
            {
                return;
            }

            // clip query bbox
            lx0 = std::max(ext.minx(),lx0);
            ly0 = std::max(ext.miny(),ly0);
            lx1 = std::min(ext.maxx(),lx1);
            ly1 = std::min(ext.maxy(),ly1);

            prj_trans.forward(lx0,ly0,lz0);
            prj_trans.forward(lx1,ly1,lz1);
            box2d<double> bbox(lx0,ly0,lx1,ly1);

            query::resolution_type res(m_.width()/m_.get_current_extent().width(),m_.height()/m_.get_current_extent().height());
            query q(bbox,res,scale_denom); //BBOX query

            std::vector<feature_type_style*> active_styles;
            std::set<std::string> names;
            attribute_collector collector(names);

            std::vector<std::string> const& style_names = lay.styles();
            // iterate through all named styles collecting active styles and attribute names
            BOOST_FOREACH(std::string const& style_name, style_names)
            {
                boost::optional<feature_type_style const&> style=m_.find_style(style_name);
                if (!style)
                {
                    std::clog << "WARNING: style '" << style_name << "' required for layer '" << lay.name() << "' does not exist.\n";
                    continue;
                }

                const std::vector<rule_type>& rules=(*style).get_rules();
                bool active_rules=false;

                BOOST_FOREACH(rule_type const& rule, rules)
                {
                    if (rule.active(scale_denom))
                    {
                        active_rules = true;
                        if (ds->type() == datasource::Vector)
                        {
                            collector(rule);
                        }
                        // TODO - in the future rasters should be able to be filtered.
                    }
                }
                if (active_rules)
                {
                    active_styles.push_back(const_cast<feature_type_style*>(&(*style)));
                }
            }

            // push all property names
            BOOST_FOREACH(std::string const& name, names)
            {
                q.add_property_name(name);
            }

            memory_datasource cache;
            bool cache_features = style_names.size()>1?true:false;
            bool first = true;

            BOOST_FOREACH (feature_type_style * style, active_styles)
            {
                std::vector<rule_type*> if_rules;
                std::vector<rule_type*> else_rules;

                std::vector<rule_type> const& rules=style->get_rules();

                BOOST_FOREACH(rule_type const& rule, rules)
                {
                    if (rule.active(scale_denom))
                    {
                        if (rule.has_else_filter())
                        {
                            else_rules.push_back(const_cast<rule_type*>(&rule));
                        }
                        else
                        {
                            if_rules.push_back(const_cast<rule_type*>(&rule));
                        }

                        if (ds->type() == datasource::Raster)
                        {
                            if (ds->params().get<double>("filter_factor",0.0) == 0.0)
                            {
                                rule_type::symbolizers const& symbols = rule.get_symbolizers();
                                rule_type::symbolizers::const_iterator symIter = symbols.begin();
                                rule_type::symbolizers::const_iterator symEnd = symbols.end();
                                for (;symIter != symEnd;++symIter)
                                {
                                    try
                                    {
                                        raster_symbolizer const& sym = boost::get<raster_symbolizer>(*symIter);
                                        std::string const& scaling = sym.get_scaling();
                                        if (scaling == "bilinear" || scaling == "bilinear8" )
                                        {
                                            // todo - allow setting custom value in symbolizer property?
                                            q.filter_factor(2.0);
                                        }
                                    }
                                    catch (const boost::bad_get &v)
                                    {
                                        // case where useless symbolizer is attached to raster layer
                                        //throw config_error("Invalid Symbolizer type supplied, only RasterSymbolizer is supported");
                                    }
                                }
                            }
                        }
                    }
                }

                // process features
                featureset_ptr fs;
                if (first)
                {
                    first = false;
                    fs = ds->features(q);
                }
                else
                {
                    fs = cache.features(q);
                }

                if (fs)
                {
                    feature_ptr feature;
                    while ((feature = fs->next()))
                    {
                        bool do_else=true;

                        if (cache_features)
                        {
                            cache.push(feature);
                        }

                        BOOST_FOREACH(rule_type * rule, if_rules )
                        {
                            expression_ptr const& expr=rule->get_filter();
                            value_type result = boost::apply_visitor(evaluate<Feature,value_type>(*feature),*expr);
                            if (result.to_bool())
                            {
                                do_else=false;
                                rule_type::symbolizers const& symbols = rule->get_symbolizers();
                                BOOST_FOREACH (symbolizer const& sym, symbols)
                                {
                                    boost::apply_visitor
                                        (symbol_dispatch(p,*feature,prj_trans),sym);
                                }
                            }
                        }
                        if (do_else)
                        {
                            BOOST_FOREACH( rule_type * rule, else_rules )
                            {
                                rule_type::symbolizers const& symbols = rule->get_symbolizers();
                                BOOST_FOREACH (symbolizer const& sym, symbols)
                                {
                                    boost::apply_visitor
                                        (symbol_dispatch(p,*feature,prj_trans),sym);
                                }
                            }
                        }
                    }
                    cache_features = false;
                }
            }
        }

        p.end_layer_processing(lay);
    }

    Map const& m_;
    double scale_factor_;
};
}

#endif //FEATURE_STYLE_PROCESSOR_HPP
<|endoftext|>


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

#include <mapnik/config.hpp>

// undef B0 to workaround https://svn.boost.org/trac/boost/ticket/10467
#pragma GCC diagnostic push
#include <mapnik/warning_ignore.hpp>
#undef B0
#include <boost/geometry/geometry.hpp>
#include <boost/geometry/geometries/register/point.hpp>
#include <boost/geometry/geometries/register/ring.hpp>
#include <boost/geometry/geometries/register/linestring.hpp>
#pragma GCC diagnostic pop
// mapnik
#include <mapnik/geometry.hpp>
#include <mapnik/coord.hpp>
#include <mapnik/geometry/box2d.hpp>

BOOST_GEOMETRY_REGISTER_POINT_2D(mapnik::geometry::point<double>, double, boost::geometry::cs::cartesian, x, y)
BOOST_GEOMETRY_REGISTER_POINT_2D (mapnik::geometry::point<std::int64_t>, std::int64_t, boost::geometry::cs::cartesian, x, y)
BOOST_GEOMETRY_REGISTER_LINESTRING_TEMPLATED(mapnik::geometry::line_string)
BOOST_GEOMETRY_REGISTER_RING_TEMPLATED(mapnik::geometry::linear_ring)
// needed by box2d<T>
BOOST_GEOMETRY_REGISTER_POINT_2D(mapnik::coord2d, double, boost::geometry::cs::cartesian, x, y)
BOOST_GEOMETRY_REGISTER_POINT_2D(mapnik::coord2f, float,  boost::geometry::cs::cartesian, x, y)

namespace mapnik {

template <typename CoordinateType>
struct interior_rings
{
    using polygon_type = mapnik::geometry::polygon<CoordinateType>;
    using iterator = typename polygon_type::iterator;
    using const_iterator = typename polygon_type::const_iterator;
    using value_type = typename polygon_type::value_type;

    interior_rings(polygon_type & poly)
        : poly_(poly) {}

    iterator begin()
    {
        auto itr = poly_.begin();
        std::advance(itr, 1);
        return itr;
    }

    iterator end() { return poly_.end();}
    const_iterator begin() const
    {
        auto itr = poly_.begin();
        std::advance(itr, 1);
        return itr;
    }

    const_iterator end() const { return poly_.end();}

    void clear()
    {
        poly_.resize(1);
    }

    void resize(std::size_t size)
    {
        poly_.resize(size + 1);
    }

    std::size_t size() const
    {
        return poly_.empty() ? 0 : poly_.size() - 1;
    }

    void push_back(value_type const& val) { poly_.push_back(val); }
    value_type& back() { return poly_.back(); }
    value_type const& back()  const { return poly_.back(); }
    polygon_type & poly_;
};

} // ns mapnik

namespace boost { namespace geometry { namespace traits {

template<> struct tag<mapnik::box2d<double> > { using type = box_tag; };
template<> struct point_type<mapnik::box2d<double> > { using type = mapnik::coord2d; };

template <>
struct indexed_access<mapnik::box2d<double>, min_corner, 0>
{
    using ct = coordinate_type<mapnik::coord2d>::type;
    static inline ct get(mapnik::box2d<double> const& b) { return b.minx();}
    static inline void set(mapnik::box2d<double> &b, ct const& value) { b.set_minx(value); }
};

template <>
struct indexed_access<mapnik::box2d<double>, min_corner, 1>
{
    using ct = coordinate_type<mapnik::coord2d>::type;
    static inline ct get(mapnik::box2d<double> const& b) { return b.miny();}
    static inline void set(mapnik::box2d<double> &b, ct const& value) { b.set_miny(value); }
};

template <>
struct indexed_access<mapnik::box2d<double>, max_corner, 0>
{
    using ct = coordinate_type<mapnik::coord2d>::type;
    static inline ct get(mapnik::box2d<double> const& b) { return b.maxx();}
    static inline void set(mapnik::box2d<double> &b, ct const& value) { b.set_maxx(value); }
};

template <>
struct indexed_access<mapnik::box2d<double>, max_corner, 1>
{
    using ct = coordinate_type<mapnik::coord2d>::type;
    static inline ct get(mapnik::box2d<double> const& b) { return b.maxy();}
    static inline void set(mapnik::box2d<double> &b , ct const& value) { b.set_maxy(value); }
};

template<typename CoordinateType>
struct tag<mapnik::geometry::polygon<CoordinateType> >
{
    using type = polygon_tag;
};

template <typename CoordinateType>
struct point_order<mapnik::geometry::linear_ring<CoordinateType> >
{
    static const order_selector value = counterclockwise;
};

template<typename CoordinateType>
struct tag<mapnik::geometry::multi_point<CoordinateType> >
{
    using type = multi_point_tag;
};

template<typename CoordinateType>
struct tag<mapnik::geometry::multi_line_string<CoordinateType> >
{
    using type = multi_linestring_tag;
};

template<typename CoordinateType>
struct tag<mapnik::geometry::multi_polygon<CoordinateType> >
{
    using type = multi_polygon_tag;
};

// ring
template <typename CoordinateType>
struct ring_const_type<mapnik::geometry::polygon<CoordinateType> >
{
    using type = typename mapnik::geometry::linear_ring<CoordinateType> const&;
};

template <typename CoordinateType>
struct ring_mutable_type<mapnik::geometry::polygon<CoordinateType> >
{
    using type = typename mapnik::geometry::linear_ring<CoordinateType>&;
};

// interior
template <typename CoordinateType>
struct interior_const_type<mapnik::geometry::polygon<CoordinateType> >
{
    using type = typename mapnik::interior_rings<CoordinateType> const;
};

template <typename CoordinateType>
struct interior_mutable_type<mapnik::geometry::polygon<CoordinateType> >
{
    using type = typename mapnik::interior_rings<CoordinateType> ;
};

template <typename CoordinateType>
struct exterior_ring<mapnik::geometry::polygon<CoordinateType> >
{
    using ring_const_type   = typename ring_const_type<mapnik::geometry::polygon<CoordinateType> >::type;
    using ring_mutable_type = typename ring_mutable_type<mapnik::geometry::polygon<CoordinateType> >::type;
    static ring_mutable_type get(mapnik::geometry::polygon<CoordinateType> & p)
    {
        if (p.empty()) p.resize(1);
        return p[0];
    }

    static ring_const_type get(mapnik::geometry::polygon<CoordinateType> const& p)
    {
        if (p.empty()) throw std::runtime_error("Exterior ring must be initialized!");
        return p[0];
    }
};

template <typename CoordinateType>
struct interior_rings<mapnik::geometry::polygon<CoordinateType> >
{
    using interior_const_type = typename interior_const_type<mapnik::geometry::polygon<CoordinateType> >::type;
    using interior_mutable_type = typename interior_mutable_type<mapnik::geometry::polygon<CoordinateType> >::type;

    static interior_const_type get(mapnik::geometry::polygon<CoordinateType> const& p)
    {
        return mapnik::interior_rings<CoordinateType>(const_cast<mapnik::geometry::polygon<CoordinateType>&>(p));
    }

    static interior_mutable_type get(mapnik::geometry::polygon<CoordinateType>& p)
    {
        return mapnik::interior_rings<CoordinateType>(p);
    }
};

template <typename CoordinateType>
struct resize<mapnik::interior_rings<CoordinateType>>
{
    static inline void apply(mapnik::interior_rings<CoordinateType> interiors, std::size_t new_size)
    {
        interiors.resize(new_size);
    }
};

template <typename CoordinateType>
struct clear<mapnik::interior_rings<CoordinateType>>
{
    static inline void apply(mapnik::interior_rings<CoordinateType> interiors)
    {
        interiors.clear();
    }
};

template <typename CoordinateType>
struct push_back<mapnik::interior_rings<CoordinateType>>
{
    template <typename Ring>
    static inline void apply(mapnik::interior_rings<CoordinateType> interiors, Ring const& ring)
    {
        interiors.push_back(ring);
    }
};

}}}

#endif //MAPNIK_BOOST_GEOMETRY_ADAPTERS_HPP
<commit_msg>add `const_interior_rings` type and stop abusing type system. (NOTE: iterator/const_iterator types are required by boost::range_iterator)<commit_after>/*****************************************************************************
 *
 * This file is part of Mapnik (c++ mapping toolkit)
 *
 * Copyright (C) 2017 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_BOOST_GEOMETRY_ADAPTERS_HPP
#define MAPNIK_BOOST_GEOMETRY_ADAPTERS_HPP

#include <mapnik/config.hpp>

// undef B0 to workaround https://svn.boost.org/trac/boost/ticket/10467
#pragma GCC diagnostic push
#include <mapnik/warning_ignore.hpp>
#undef B0
#include <boost/geometry/geometry.hpp>
#include <boost/geometry/geometries/register/point.hpp>
#include <boost/geometry/geometries/register/ring.hpp>
#include <boost/geometry/geometries/register/linestring.hpp>
#pragma GCC diagnostic pop
// mapnik
#include <mapnik/geometry.hpp>
#include <mapnik/coord.hpp>
#include <mapnik/geometry/box2d.hpp>

BOOST_GEOMETRY_REGISTER_POINT_2D(mapnik::geometry::point<double>, double, boost::geometry::cs::cartesian, x, y)
BOOST_GEOMETRY_REGISTER_POINT_2D (mapnik::geometry::point<std::int64_t>, std::int64_t, boost::geometry::cs::cartesian, x, y)
BOOST_GEOMETRY_REGISTER_LINESTRING_TEMPLATED(mapnik::geometry::line_string)
BOOST_GEOMETRY_REGISTER_RING_TEMPLATED(mapnik::geometry::linear_ring)
// needed by box2d<T>
BOOST_GEOMETRY_REGISTER_POINT_2D(mapnik::coord2d, double, boost::geometry::cs::cartesian, x, y)
BOOST_GEOMETRY_REGISTER_POINT_2D(mapnik::coord2f, float,  boost::geometry::cs::cartesian, x, y)

namespace mapnik {

template <typename CoordinateType>
struct const_interior_rings
{
    using polygon_type = mapnik::geometry::polygon<CoordinateType> const;
    using const_iterator = typename polygon_type::const_iterator;
    using iterator = const_iterator; // needed by boost::range_iterator
    using value_type = typename polygon_type::value_type;

    const_interior_rings(polygon_type const& poly)
        : poly_(poly) {}

    const_iterator begin() const
    {
        auto itr = poly_.cbegin();
        std::advance(itr, 1);
        return itr;
    }

    const_iterator end() const { return poly_.cend();}

    std::size_t size() const
    {
        return poly_.empty() ? 0 : poly_.size() - 1;
    }

    value_type const& back()  const { return poly_.back(); }
    polygon_type const& poly_;
};

template <typename CoordinateType>
struct interior_rings
{
    using polygon_type = mapnik::geometry::polygon<CoordinateType>;
    using iterator = typename polygon_type::iterator;
    using const_iterator = typename polygon_type::const_iterator;
    using value_type = typename polygon_type::value_type;

    interior_rings(polygon_type & poly)
        : poly_(poly) {}

    iterator begin()
    {
        auto itr = poly_.begin();
        std::advance(itr, 1);
        return itr;
    }

    iterator end() { return poly_.end();}
    const_iterator begin() const
    {
        auto itr = poly_.cbegin();
        std::advance(itr, 1);
        return itr;
    }

    const_iterator end() const { return poly_.cend();}

    void clear()
    {
        poly_.resize(1);
    }

    void resize(std::size_t size)
    {
        poly_.resize(size + 1);
    }

    std::size_t size() const
    {
        return poly_.empty() ? 0 : poly_.size() - 1;
    }

    void push_back(value_type const& val) { poly_.push_back(val); }
    value_type& back() { return poly_.back(); }
    value_type const& back()  const { return poly_.back(); }
    polygon_type & poly_;
};

} // ns mapnik

namespace boost { namespace geometry { namespace traits {

template<> struct tag<mapnik::box2d<double> > { using type = box_tag; };
template<> struct point_type<mapnik::box2d<double> > { using type = mapnik::coord2d; };

template <>
struct indexed_access<mapnik::box2d<double>, min_corner, 0>
{
    using ct = coordinate_type<mapnik::coord2d>::type;
    static inline ct get(mapnik::box2d<double> const& b) { return b.minx();}
    static inline void set(mapnik::box2d<double> &b, ct const& value) { b.set_minx(value); }
};

template <>
struct indexed_access<mapnik::box2d<double>, min_corner, 1>
{
    using ct = coordinate_type<mapnik::coord2d>::type;
    static inline ct get(mapnik::box2d<double> const& b) { return b.miny();}
    static inline void set(mapnik::box2d<double> &b, ct const& value) { b.set_miny(value); }
};

template <>
struct indexed_access<mapnik::box2d<double>, max_corner, 0>
{
    using ct = coordinate_type<mapnik::coord2d>::type;
    static inline ct get(mapnik::box2d<double> const& b) { return b.maxx();}
    static inline void set(mapnik::box2d<double> &b, ct const& value) { b.set_maxx(value); }
};

template <>
struct indexed_access<mapnik::box2d<double>, max_corner, 1>
{
    using ct = coordinate_type<mapnik::coord2d>::type;
    static inline ct get(mapnik::box2d<double> const& b) { return b.maxy();}
    static inline void set(mapnik::box2d<double> &b , ct const& value) { b.set_maxy(value); }
};

template<typename CoordinateType>
struct tag<mapnik::geometry::polygon<CoordinateType> >
{
    using type = polygon_tag;
};

template <typename CoordinateType>
struct point_order<mapnik::geometry::linear_ring<CoordinateType> >
{
    static const order_selector value = counterclockwise;
};

template<typename CoordinateType>
struct tag<mapnik::geometry::multi_point<CoordinateType> >
{
    using type = multi_point_tag;
};

template<typename CoordinateType>
struct tag<mapnik::geometry::multi_line_string<CoordinateType> >
{
    using type = multi_linestring_tag;
};

template<typename CoordinateType>
struct tag<mapnik::geometry::multi_polygon<CoordinateType> >
{
    using type = multi_polygon_tag;
};

// ring
template <typename CoordinateType>
struct ring_const_type<mapnik::geometry::polygon<CoordinateType> >
{
    using type = typename mapnik::geometry::linear_ring<CoordinateType> const&;
};

template <typename CoordinateType>
struct ring_mutable_type<mapnik::geometry::polygon<CoordinateType> >
{
    using type = typename mapnik::geometry::linear_ring<CoordinateType>&;
};

// interior
template <typename CoordinateType>
struct interior_const_type<mapnik::geometry::polygon<CoordinateType>>
{
    using type = typename mapnik::const_interior_rings<CoordinateType> const;
};

template <typename CoordinateType>
struct interior_mutable_type<mapnik::geometry::polygon<CoordinateType> >
{
    using type = typename mapnik::interior_rings<CoordinateType> ;
};

template <typename CoordinateType>
struct exterior_ring<mapnik::geometry::polygon<CoordinateType> >
{
    using ring_const_type   = typename ring_const_type<mapnik::geometry::polygon<CoordinateType> >::type;
    using ring_mutable_type = typename ring_mutable_type<mapnik::geometry::polygon<CoordinateType> >::type;
    static ring_mutable_type get(mapnik::geometry::polygon<CoordinateType> & p)
    {
        if (p.empty()) p.resize(1);
        return p[0];
    }

    static ring_const_type get(mapnik::geometry::polygon<CoordinateType> const& p)
    {
        if (p.empty()) throw std::runtime_error("Exterior ring must be initialized!");
        return p[0];
    }
};

template <typename CoordinateType>
struct interior_rings<mapnik::geometry::polygon<CoordinateType> >
{
    using interior_const_type = typename interior_const_type<mapnik::geometry::polygon<CoordinateType> >::type;
    using interior_mutable_type = typename interior_mutable_type<mapnik::geometry::polygon<CoordinateType> >::type;

    static interior_const_type get(mapnik::geometry::polygon<CoordinateType> const& p)
    {
        return mapnik::const_interior_rings<CoordinateType>(p);
    }

    static interior_mutable_type get(mapnik::geometry::polygon<CoordinateType>& p)
    {
        return mapnik::interior_rings<CoordinateType>(p);
    }
};

template <typename CoordinateType>
struct resize<mapnik::interior_rings<CoordinateType>>
{
    static inline void apply(mapnik::interior_rings<CoordinateType> interiors, std::size_t new_size)
    {
        interiors.resize(new_size);
    }
};

template <typename CoordinateType>
struct clear<mapnik::interior_rings<CoordinateType>>
{
    static inline void apply(mapnik::interior_rings<CoordinateType> interiors)
    {
        interiors.clear();
    }
};

template <typename CoordinateType>
struct push_back<mapnik::interior_rings<CoordinateType>>
{
    template <typename Ring>
    static inline void apply(mapnik::interior_rings<CoordinateType> interiors, Ring const& ring)
    {
        interiors.push_back(ring);
    }
};

}}}

#endif //MAPNIK_BOOST_GEOMETRY_ADAPTERS_HPP
<|endoftext|>


<commit_before>#pragma once

#include <optional>
#include <string>
#include <string_view>
#include <variant>

namespace sdbusplus
{

namespace message
{

namespace details
{

/** Simple wrapper class for std::string to allow conversion to and from an
 *  alternative typename. */
struct string_wrapper
{
    std::string str;

    string_wrapper() = default;
    string_wrapper(const string_wrapper&) = default;
    string_wrapper& operator=(const string_wrapper&) = default;
    string_wrapper(string_wrapper&&) = default;
    string_wrapper& operator=(string_wrapper&&) = default;
    ~string_wrapper() = default;

    string_wrapper(const std::string& str) : str(str)
    {}
    string_wrapper(std::string&& str) : str(std::move(str))
    {}

    operator const std::string&() const volatile&
    {
        return const_cast<const string_wrapper*>(this)->str;
    }
    operator std::string&&() &&
    {
        return std::move(str);
    }

    bool operator==(const string_wrapper& r) const
    {
        return str == r.str;
    }
    bool operator!=(const string_wrapper& r) const
    {
        return str != r.str;
    }
    bool operator<(const string_wrapper& r) const
    {
        return str < r.str;
    }
    bool operator==(const std::string& r) const
    {
        return str == r;
    }
    bool operator!=(const std::string& r) const
    {
        return str != r;
    }
    bool operator<(const std::string& r) const
    {
        return str < r;
    }

    friend bool operator==(const std::string& l, const string_wrapper& r)
    {
        return l == r.str;
    }
    friend bool operator!=(const std::string& l, const string_wrapper& r)
    {
        return l != r.str;
    }
    friend bool operator<(const std::string& l, const string_wrapper& r)
    {
        return l < r.str;
    }
};

/** Simple wrapper class for std::string to allow conversion to and from an
 *  alternative typename. */
struct string_path_wrapper
{
    std::string str;

    string_path_wrapper() = default;
    string_path_wrapper(const string_path_wrapper&) = default;
    string_path_wrapper& operator=(const string_path_wrapper&) = default;
    string_path_wrapper(string_path_wrapper&&) = default;
    string_path_wrapper& operator=(string_path_wrapper&&) = default;
    ~string_path_wrapper() = default;

    string_path_wrapper(const std::string& str) : str(str)
    {}
    string_path_wrapper(std::string&& str) : str(std::move(str))
    {}

    operator const std::string&() const volatile&
    {
        return const_cast<const string_path_wrapper*>(this)->str;
    }
    operator std::string&&() &&
    {
        return std::move(str);
    }

    bool operator==(const string_path_wrapper& r) const
    {
        return str == r.str;
    }
    bool operator!=(const string_path_wrapper& r) const
    {
        return str != r.str;
    }
    bool operator<(const string_path_wrapper& r) const
    {
        return str < r.str;
    }
    bool operator==(const std::string& r) const
    {
        return str == r;
    }
    bool operator!=(const std::string& r) const
    {
        return str != r;
    }
    bool operator<(const std::string& r) const
    {
        return str < r;
    }

    friend bool operator==(const std::string& l, const string_path_wrapper& r)
    {
        return l == r.str;
    }
    friend bool operator!=(const std::string& l, const string_path_wrapper& r)
    {
        return l != r.str;
    }
    friend bool operator<(const std::string& l, const string_path_wrapper& r)
    {
        return l < r.str;
    }

    std::string filename() const;
    string_path_wrapper parent_path() const;
    string_path_wrapper operator/(std::string_view) const;
    string_path_wrapper& operator/=(std::string_view);
};

/** Typename for sdbus SIGNATURE types. */
struct signature_type
{};
/** Typename for sdbus UNIX_FD types. */
struct unix_fd_type
{
    int fd;

    unix_fd_type() = default;
    unix_fd_type(int f) : fd(f)
    {}

    operator int() const
    {
        return fd;
    }
};

} // namespace details

/** std::string wrapper for OBJECT_PATH. */
using object_path = details::string_path_wrapper;
/** std::string wrapper for SIGNATURE. */
using signature = details::string_wrapper;
using unix_fd = details::unix_fd_type;

namespace details
{

template <typename T>
struct convert_from_string
{
    static auto op(const std::string&) noexcept = delete;
};

template <typename T>
struct convert_to_string
{
    static std::string op(T) = delete;
};

} // namespace details

/** @brief Convert from a string to a native type.
 *
 *  Some C++ types cannot be represented directly on dbus, so we encode
 *  them as strings.  Enums are the primary example of this.  This is a
 *  template function prototype for the conversion from string functions.
 *
 *  @return A std::optional<T> containing the value if conversion is possible.
 */
template <typename T>
auto convert_from_string(const std::string& str) noexcept
{
    return details::convert_from_string<T>::op(str);
};

/** @brief Convert from a native type to a string.
 *
 *  Some C++ types cannot be represented directly on dbus, so we encode
 *  them as strings.  Enums are the primary example of this.  This is a
 *  template function prototype for the conversion to string functions.
 *
 *  @return A std::string containing an encoding of the value, if conversion is
 *          possible.
 */
template <typename T>
std::string convert_to_string(T t)
{
    return details::convert_to_string<T>::op(t);
}

namespace details
{
// SFINAE templates to determine if convert_from_string exists for a type.
template <typename T>
auto has_convert_from_string_helper(T)
    -> decltype(convert_from_string<T>::op(std::declval<std::string>()),
                std::true_type());
auto has_convert_from_string_helper(...) -> std::false_type;

template <typename T>
struct has_convert_from_string :
    decltype(has_convert_from_string_helper(std::declval<T>()))
{};

template <typename T>
inline constexpr bool has_convert_from_string_v =
    has_convert_from_string<T>::value;

// Specialization of 'convert_from_string' for variant.
template <typename... Types>
struct convert_from_string<std::variant<Types...>>
{
    static auto op(const std::string& str)
        -> std::optional<std::variant<Types...>>
    {
        if constexpr (0 < sizeof...(Types))
        {
            return process<Types...>(str);
        }
        return {};
    }

    // We need to iterate through all the variant types and find
    // the one which matches the contents of the string.  Often,
    // a variant can contain both a convertible-type (ie. enum) and
    // a string, so we need to iterate through all the convertible-types
    // first and convert to string as a last resort.
    template <typename T, typename... Args>
    static auto process(const std::string& str)
        -> std::optional<std::variant<Types...>>
    {
        // If convert_from_string exists for the type, attempt it.
        if constexpr (has_convert_from_string_v<T>)
        {
            auto r = convert_from_string<T>::op(str);
            if (r)
            {
                return r;
            }
        }

        // If there are any more types in the variant, try them.
        if constexpr (0 < sizeof...(Args))
        {
            auto r = process<Args...>(str);
            if (r)
            {
                return r;
            }
        }

        // Otherwise, if this is a string, do last-resort conversion.
        if constexpr (std::is_same_v<std::string, std::remove_cv_t<T>>)
        {
            return str;
        }

        return {};
    }
};

} // namespace details

/** Export template helper to determine if a type has convert_from_string. */
template <typename T>
inline constexpr bool has_convert_from_string_v =
    details::has_convert_from_string_v<T>;

} // namespace message
} // namespace sdbusplus

namespace std
{

/** Overload of std::hash for details::string_wrappers */
template <>
struct hash<sdbusplus::message::details::string_wrapper>
{
    using argument_type = sdbusplus::message::details::string_wrapper;
    using result_type = std::size_t;

    result_type operator()(argument_type const& s) const
    {
        return hash<std::string>()(s.str);
    }
};

/** Overload of std::hash for details::string_wrappers */
template <>
struct hash<sdbusplus::message::details::string_path_wrapper>
{
    using argument_type = sdbusplus::message::details::string_path_wrapper;
    using result_type = std::size_t;

    result_type operator()(argument_type const& s) const
    {
        return hash<std::string>()(s.str);
    }
};

} // namespace std
<commit_msg>native_types: Fix pendantic error<commit_after>#pragma once

#include <optional>
#include <string>
#include <string_view>
#include <variant>

namespace sdbusplus
{

namespace message
{

namespace details
{

/** Simple wrapper class for std::string to allow conversion to and from an
 *  alternative typename. */
struct string_wrapper
{
    std::string str;

    string_wrapper() = default;
    string_wrapper(const string_wrapper&) = default;
    string_wrapper& operator=(const string_wrapper&) = default;
    string_wrapper(string_wrapper&&) = default;
    string_wrapper& operator=(string_wrapper&&) = default;
    ~string_wrapper() = default;

    string_wrapper(const std::string& str) : str(str)
    {}
    string_wrapper(std::string&& str) : str(std::move(str))
    {}

    operator const std::string&() const volatile&
    {
        return const_cast<const string_wrapper*>(this)->str;
    }
    operator std::string&&() &&
    {
        return std::move(str);
    }

    bool operator==(const string_wrapper& r) const
    {
        return str == r.str;
    }
    bool operator!=(const string_wrapper& r) const
    {
        return str != r.str;
    }
    bool operator<(const string_wrapper& r) const
    {
        return str < r.str;
    }
    bool operator==(const std::string& r) const
    {
        return str == r;
    }
    bool operator!=(const std::string& r) const
    {
        return str != r;
    }
    bool operator<(const std::string& r) const
    {
        return str < r;
    }

    friend bool operator==(const std::string& l, const string_wrapper& r)
    {
        return l == r.str;
    }
    friend bool operator!=(const std::string& l, const string_wrapper& r)
    {
        return l != r.str;
    }
    friend bool operator<(const std::string& l, const string_wrapper& r)
    {
        return l < r.str;
    }
};

/** Simple wrapper class for std::string to allow conversion to and from an
 *  alternative typename. */
struct string_path_wrapper
{
    std::string str;

    string_path_wrapper() = default;
    string_path_wrapper(const string_path_wrapper&) = default;
    string_path_wrapper& operator=(const string_path_wrapper&) = default;
    string_path_wrapper(string_path_wrapper&&) = default;
    string_path_wrapper& operator=(string_path_wrapper&&) = default;
    ~string_path_wrapper() = default;

    string_path_wrapper(const std::string& str) : str(str)
    {}
    string_path_wrapper(std::string&& str) : str(std::move(str))
    {}

    operator const std::string&() const volatile&
    {
        return const_cast<const string_path_wrapper*>(this)->str;
    }
    operator std::string&&() &&
    {
        return std::move(str);
    }

    bool operator==(const string_path_wrapper& r) const
    {
        return str == r.str;
    }
    bool operator!=(const string_path_wrapper& r) const
    {
        return str != r.str;
    }
    bool operator<(const string_path_wrapper& r) const
    {
        return str < r.str;
    }
    bool operator==(const std::string& r) const
    {
        return str == r;
    }
    bool operator!=(const std::string& r) const
    {
        return str != r;
    }
    bool operator<(const std::string& r) const
    {
        return str < r;
    }

    friend bool operator==(const std::string& l, const string_path_wrapper& r)
    {
        return l == r.str;
    }
    friend bool operator!=(const std::string& l, const string_path_wrapper& r)
    {
        return l != r.str;
    }
    friend bool operator<(const std::string& l, const string_path_wrapper& r)
    {
        return l < r.str;
    }

    std::string filename() const;
    string_path_wrapper parent_path() const;
    string_path_wrapper operator/(std::string_view) const;
    string_path_wrapper& operator/=(std::string_view);
};

/** Typename for sdbus SIGNATURE types. */
struct signature_type
{};
/** Typename for sdbus UNIX_FD types. */
struct unix_fd_type
{
    int fd;

    unix_fd_type() = default;
    unix_fd_type(int f) : fd(f)
    {}

    operator int() const
    {
        return fd;
    }
};

} // namespace details

/** std::string wrapper for OBJECT_PATH. */
using object_path = details::string_path_wrapper;
/** std::string wrapper for SIGNATURE. */
using signature = details::string_wrapper;
using unix_fd = details::unix_fd_type;

namespace details
{

template <typename T>
struct convert_from_string
{
    static auto op(const std::string&) noexcept = delete;
};

template <typename T>
struct convert_to_string
{
    static std::string op(T) = delete;
};

} // namespace details

/** @brief Convert from a string to a native type.
 *
 *  Some C++ types cannot be represented directly on dbus, so we encode
 *  them as strings.  Enums are the primary example of this.  This is a
 *  template function prototype for the conversion from string functions.
 *
 *  @return A std::optional<T> containing the value if conversion is possible.
 */
template <typename T>
auto convert_from_string(const std::string& str) noexcept
{
    return details::convert_from_string<T>::op(str);
}

/** @brief Convert from a native type to a string.
 *
 *  Some C++ types cannot be represented directly on dbus, so we encode
 *  them as strings.  Enums are the primary example of this.  This is a
 *  template function prototype for the conversion to string functions.
 *
 *  @return A std::string containing an encoding of the value, if conversion is
 *          possible.
 */
template <typename T>
std::string convert_to_string(T t)
{
    return details::convert_to_string<T>::op(t);
}

namespace details
{
// SFINAE templates to determine if convert_from_string exists for a type.
template <typename T>
auto has_convert_from_string_helper(T)
    -> decltype(convert_from_string<T>::op(std::declval<std::string>()),
                std::true_type());
auto has_convert_from_string_helper(...) -> std::false_type;

template <typename T>
struct has_convert_from_string :
    decltype(has_convert_from_string_helper(std::declval<T>()))
{};

template <typename T>
inline constexpr bool has_convert_from_string_v =
    has_convert_from_string<T>::value;

// Specialization of 'convert_from_string' for variant.
template <typename... Types>
struct convert_from_string<std::variant<Types...>>
{
    static auto op(const std::string& str)
        -> std::optional<std::variant<Types...>>
    {
        if constexpr (0 < sizeof...(Types))
        {
            return process<Types...>(str);
        }
        return {};
    }

    // We need to iterate through all the variant types and find
    // the one which matches the contents of the string.  Often,
    // a variant can contain both a convertible-type (ie. enum) and
    // a string, so we need to iterate through all the convertible-types
    // first and convert to string as a last resort.
    template <typename T, typename... Args>
    static auto process(const std::string& str)
        -> std::optional<std::variant<Types...>>
    {
        // If convert_from_string exists for the type, attempt it.
        if constexpr (has_convert_from_string_v<T>)
        {
            auto r = convert_from_string<T>::op(str);
            if (r)
            {
                return r;
            }
        }

        // If there are any more types in the variant, try them.
        if constexpr (0 < sizeof...(Args))
        {
            auto r = process<Args...>(str);
            if (r)
            {
                return r;
            }
        }

        // Otherwise, if this is a string, do last-resort conversion.
        if constexpr (std::is_same_v<std::string, std::remove_cv_t<T>>)
        {
            return str;
        }

        return {};
    }
};

} // namespace details

/** Export template helper to determine if a type has convert_from_string. */
template <typename T>
inline constexpr bool has_convert_from_string_v =
    details::has_convert_from_string_v<T>;

} // namespace message
} // namespace sdbusplus

namespace std
{

/** Overload of std::hash for details::string_wrappers */
template <>
struct hash<sdbusplus::message::details::string_wrapper>
{
    using argument_type = sdbusplus::message::details::string_wrapper;
    using result_type = std::size_t;

    result_type operator()(argument_type const& s) const
    {
        return hash<std::string>()(s.str);
    }
};

/** Overload of std::hash for details::string_wrappers */
template <>
struct hash<sdbusplus::message::details::string_path_wrapper>
{
    using argument_type = sdbusplus::message::details::string_path_wrapper;
    using result_type = std::size_t;

    result_type operator()(argument_type const& s) const
    {
        return hash<std::string>()(s.str);
    }
};

} // namespace std
<|endoftext|>


<commit_before>#include "UniformNegativeSampleGenerator.h"

vector<int>* UniformNegativeSampleGenerator::generate(RecDat* rec_dat){
  if(!filter_repeats_){
    int learnt = 0;
    samples.clear();
    int user_activity = train_matrix_->row_size(rec_dat->user);
    while(learnt < negative_rate_ && learnt<(int)items_->size()-user_activity){
      int item = items_->at((int)(rnd_.get()*(items_->size())));
      if(!train_matrix_->has_value(rec_dat->user,item)){
        learnt++;
        samples.push_back(item);
      }
    }
    return &samples;
  } else { //no repeating items in the negative sample set
    for(int i=indices_.size();i<items_->size();i++){
      indices_.push_back(i);
    }
    int number_of_generated = 0;
    int available = items_->size(); //==indices_.size()
    samples.clear();
    while(number_of_generated < negative_rate_ && available>0){
      int idx_idx = ((int)(rnd_.get()*available));
      int idx = indices_[idx_idx];
      int item = items_->at(idx);
      if(!train_matrix_->has_value(rec_dat->user,item)){
        number_of_generated++;
        samples.push_back(item);
      }
      indices_[idx_idx]=indices_[available-1];
      indices_[available-1] = idx;
      available--;
    }
    return &samples;
  }
}

<commit_msg>remove unsigned warning<commit_after>#include "UniformNegativeSampleGenerator.h"

vector<int>* UniformNegativeSampleGenerator::generate(RecDat* rec_dat){
  if(!filter_repeats_){
    int learnt = 0;
    samples.clear();
    int user_activity = train_matrix_->row_size(rec_dat->user);
    while(learnt < negative_rate_ && learnt<(int)items_->size()-user_activity){
      int item = items_->at((int)(rnd_.get()*(items_->size())));
      if(!train_matrix_->has_value(rec_dat->user,item)){
        learnt++;
        samples.push_back(item);
      }
    }
    return &samples;
  } else { //no repeating items in the negative sample set
    for(uint i=indices_.size();i<items_->size();i++){
      indices_.push_back(i);
    }
    int number_of_generated = 0;
    int available = items_->size(); //==indices_.size()
    samples.clear();
    while(number_of_generated < negative_rate_ && available>0){
      int idx_idx = ((int)(rnd_.get()*available));
      int idx = indices_[idx_idx];
      int item = items_->at(idx);
      if(!train_matrix_->has_value(rec_dat->user,item)){
        number_of_generated++;
        samples.push_back(item);
      }
      indices_[idx_idx]=indices_[available-1];
      indices_[available-1] = idx;
      available--;
    }
    return &samples;
  }
}

<|endoftext|>