<commit_before>#include "board.hpp"

#include <cstring>

#include <boost/lexical_cast.hpp>

#include "exception.hpp"
#include "walk_move.hpp"

namespace Quoridor {

Board::Board(int row_num, int col_num) : row_num_(), col_num_(),
        sides_(), pawn_sides_(), walls_(),
        nodes_(row_num * col_num), bg_(row_num, col_num)
{
    set_size(row_num, col_num);
    sides_.push_back(std::pair<int, int>(0, 0));
    sides_.push_back(std::pair<int, int>(2, 0));
    sides_.push_back(std::pair<int, int>(1, 0));
    sides_.push_back(std::pair<int, int>(3, 0));
}

Board::~Board()
{
}

void Board::set_size(int row_num, int col_num)
{
    if (row_num <= 0) {
        throw Exception("illegal row number: "
                + boost::lexical_cast<std::string>(row_num));
    }
    if (col_num <= 0) {
        throw Exception("illegal column number: "
                + boost::lexical_cast<std::string>(col_num));
    }
    row_num_ = row_num;
    col_num_ = col_num;
}

int Board::next_side() const
{
    for (auto &side : sides_) {
        if (side.second == 0) {
            side.second = 1;
            return side.first;
        }
    }
    return -1;
}

int Board::add_pawn(std::shared_ptr<Pawn> pawn)
{
    pawn_sides_[pawn] = next_side();
    int n;

    switch (pawn_sides_[pawn]) {
    case 0:
        n = col_num_ / 2;
        break;
    case 1:
        n = row_num_ / 2;
        break;
    case 2:
        n = (row_num_ - 1) * col_num_ + col_num_ / 2;
        break;
    case 3:
        n = (row_num_ / 2) * col_num_ + col_num_ - 1;
        break;
    default:
        throw Exception("invalid pawn side: "
                + boost::lexical_cast<std::string>(pawn_sides_[pawn]));
    }

    occ_nodes_[n] = pawn;
    pawn_node_[pawn] = n;

    return 0;
}

void Board::add_occupied(const pos_t &pos, std::shared_ptr<Pawn> pawn)
{
    int n = pos.row * col_num_ + pos.col;

    if (occ_nodes_.count(n) > 0) {
        throw Exception("cell (" + boost::lexical_cast<std::string>(pos.row)
                + ":" + boost::lexical_cast<std::string>(pos.col)
                + ") is already occupied");
    }

    occ_nodes_[n] = pawn;
    pawn_node_[pawn] = n;
}

void Board::rm_occupied(const pos_t &pos)
{
    int n = pos.row * col_num_ + pos.col;
    occ_nodes_.erase(n);
}

pos_t Board::pawn_pos(std::shared_ptr<Pawn> pawn) const
{
    int node = pawn_node_.at(pawn);
    pos_t pos;
    pos.row = row(node);
    pos.col = col(node);
    return pos;
}

bool Board::is_at_opposite_side(std::shared_ptr<Pawn> pawn) const
{
    int n = pawn_node_.at(pawn);
    switch (pawn_sides_.at(pawn)) {
    case 0:
        return row(n) == row_num_ - 1;
    case 1:
        return col(n) == col_num_ - 1;
    case 2:
        return row(n) == 0;
    case 3:
        return col(n) == 0;
    default:
        throw Exception("invalid board side: "
                + boost::lexical_cast<std::string>(pawn_sides_.at(pawn)));
    }
}

int Board::recalc_dir(int dir, std::shared_ptr<Pawn> pawn)
{
    return (dir + pawn_sides_[pawn]) % 4;
}

int Board::make_walking_move(int dir, std::shared_ptr<Pawn> pawn)
{
    dir = recalc_dir(dir, pawn);
    int cur_node = pawn_node_[pawn];
    int goal_node = cur_node;
    int r_goal_node = cur_node;

    switch (dir) {
        case WalkMove::Direction::kForward:
        goal_node += col_num_;
        r_goal_node = goal_node + col_num_;
        break;
    case WalkMove::Direction::kRight:
        ++goal_node;
        r_goal_node = goal_node + 1;
        break;
    case WalkMove::Direction::kBackward:
        goal_node -= col_num_;
        r_goal_node = goal_node - col_num_;
        break;
    case WalkMove::Direction::kLeft:
        --goal_node;
        r_goal_node = goal_node - 1;
        break;
    case WalkMove::Direction::kEnd:
    default:
        return -1;
    }

    if (is_possible_move(cur_node, goal_node)) {
        // the goal node is occupied by another pawn
        if (occ_nodes_.count(goal_node) > 0) {
            if (is_possible_move(goal_node, r_goal_node)) {
                goal_node = r_goal_node;
            }
            else {
                return -2;
            }
        }
    }
    else {
        return -1;
    }

    // update pawn's position
    occ_nodes_.erase(pawn_node_[pawn]);
    pawn_node_[pawn] = goal_node;
    occ_nodes_[goal_node] = pawn;

    return 0;
}

int Board::add_wall(const Wall &wall)
{
    int line_lim = wall.orientation() ? col_num() : row_num() - 1;
    int start_pos_lim = (wall.orientation() ? row_num() : col_num()) - 1;
    if ((wall.line() >= line_lim)
            || (wall.end_pos() >= start_pos_lim)) {
        return -1;
    }

    if (wall_intersects(wall)) {
        return -2;
    }

    walls_[wall.orientation()][wall.line()].insert(std::map<int, Wall>::value_type(wall.start_pos(), Wall(wall)));

    int node1;
    int node2;
    for (int i = 0; i < wall.cnt(); ++i) {
        if (wall.orientation() == 0) {
            node1 = wall.line() * row_num_ + wall.start_pos() + i;
            node2 = (wall.line() + 1) * row_num_ + wall.start_pos() + i;
        }
        else {
            node1 = (wall.start_pos() + i) * row_num_ + wall.line();
            node2 = (wall.start_pos() + i) * row_num_ + wall.line() + 1;
        }
        std::cout << "removing edge " << node1 << ":" << node2 << std::endl;
        bg_.remove_edges(node1, node2);
    }

    return 0;
}

bool Board::wall_intersects(const Wall &wall) const
{
    // check intersections
    if (walls_.count(1 - wall.orientation()) > 0) {
        for (int i = 0; i < wall.cnt() - 1; ++i) {
            // there are walls on the intersected line
            if (walls_.at(1 - wall.orientation()).count(wall.start_pos()) != 0) {
                auto line_walls = walls_.at(1 - wall.orientation()).at(wall.start_pos() + i);
                auto it = line_walls.upper_bound(wall.line());

                // all walls on the line are settled before new wall
                if (it == line_walls.end()) {
                    auto rit = line_walls.rbegin();
                    if (rit->second.end_pos() >= wall.line()) {
                        return true;
                    }
                }
                else if (it != line_walls.begin()) {
                    --it;
                    if (it->second.end_pos() >= wall.line()) {
                        return true;
                    }
                }
            }
        }
    }

    // check overlaps
    if (walls_.count(wall.orientation()) > 0) {
        if (walls_.at(wall.orientation()).count(wall.line()) != 0) {
            auto line_walls = walls_.at(wall.orientation()).at(wall.line());
            auto it = line_walls.upper_bound(wall.start_pos());

            // all walls on the line are settled before new wall
            if (it == line_walls.end()) {
                auto rit = line_walls.rbegin();
                if (rit->second.end_pos() >= wall.start_pos()) {
                    return true;
                }
            }
            else {
                // check if new wall overlaps over next wall on the line
                if (wall.end_pos() >= it->second.start_pos()) {
                    return true;
                }

                if (it != line_walls.begin()) {
                    --it;
                    if (it->second.end_pos() >= wall.start_pos()) {
                        return true;
                    }
                }
            }
        }
    }

    return false;
}

bool Board::is_possible_move(int cur_node, int goal_node) const
{
    return bg_.is_neighbours(cur_node, goal_node);
}

}  /* namespace Quoridor */
<commit_msg>Fix bug with wall on the last Board column.<commit_after>#include "board.hpp"

#include <cstring>

#include <boost/lexical_cast.hpp>

#include "exception.hpp"
#include "walk_move.hpp"

namespace Quoridor {

Board::Board(int row_num, int col_num) : row_num_(), col_num_(),
        sides_(), pawn_sides_(), walls_(),
        nodes_(row_num * col_num), bg_(row_num, col_num)
{
    set_size(row_num, col_num);
    sides_.push_back(std::pair<int, int>(0, 0));
    sides_.push_back(std::pair<int, int>(2, 0));
    sides_.push_back(std::pair<int, int>(1, 0));
    sides_.push_back(std::pair<int, int>(3, 0));
}

Board::~Board()
{
}

void Board::set_size(int row_num, int col_num)
{
    if (row_num <= 0) {
        throw Exception("illegal row number: "
                + boost::lexical_cast<std::string>(row_num));
    }
    if (col_num <= 0) {
        throw Exception("illegal column number: "
                + boost::lexical_cast<std::string>(col_num));
    }
    row_num_ = row_num;
    col_num_ = col_num;
}

int Board::next_side() const
{
    for (auto &side : sides_) {
        if (side.second == 0) {
            side.second = 1;
            return side.first;
        }
    }
    return -1;
}

int Board::add_pawn(std::shared_ptr<Pawn> pawn)
{
    pawn_sides_[pawn] = next_side();
    int n;

    switch (pawn_sides_[pawn]) {
    case 0:
        n = col_num_ / 2;
        break;
    case 1:
        n = row_num_ / 2;
        break;
    case 2:
        n = (row_num_ - 1) * col_num_ + col_num_ / 2;
        break;
    case 3:
        n = (row_num_ / 2) * col_num_ + col_num_ - 1;
        break;
    default:
        throw Exception("invalid pawn side: "
                + boost::lexical_cast<std::string>(pawn_sides_[pawn]));
    }

    occ_nodes_[n] = pawn;
    pawn_node_[pawn] = n;

    return 0;
}

void Board::add_occupied(const pos_t &pos, std::shared_ptr<Pawn> pawn)
{
    int n = pos.row * col_num_ + pos.col;

    if (occ_nodes_.count(n) > 0) {
        throw Exception("cell (" + boost::lexical_cast<std::string>(pos.row)
                + ":" + boost::lexical_cast<std::string>(pos.col)
                + ") is already occupied");
    }

    occ_nodes_[n] = pawn;
    pawn_node_[pawn] = n;
}

void Board::rm_occupied(const pos_t &pos)
{
    int n = pos.row * col_num_ + pos.col;
    occ_nodes_.erase(n);
}

pos_t Board::pawn_pos(std::shared_ptr<Pawn> pawn) const
{
    int node = pawn_node_.at(pawn);
    pos_t pos;
    pos.row = row(node);
    pos.col = col(node);
    return pos;
}

bool Board::is_at_opposite_side(std::shared_ptr<Pawn> pawn) const
{
    int n = pawn_node_.at(pawn);
    switch (pawn_sides_.at(pawn)) {
    case 0:
        return row(n) == row_num_ - 1;
    case 1:
        return col(n) == col_num_ - 1;
    case 2:
        return row(n) == 0;
    case 3:
        return col(n) == 0;
    default:
        throw Exception("invalid board side: "
                + boost::lexical_cast<std::string>(pawn_sides_.at(pawn)));
    }
}

int Board::recalc_dir(int dir, std::shared_ptr<Pawn> pawn)
{
    return (dir + pawn_sides_[pawn]) % 4;
}

int Board::make_walking_move(int dir, std::shared_ptr<Pawn> pawn)
{
    dir = recalc_dir(dir, pawn);
    int cur_node = pawn_node_[pawn];
    int goal_node = cur_node;
    int r_goal_node = cur_node;

    switch (dir) {
        case WalkMove::Direction::kForward:
        goal_node += col_num_;
        r_goal_node = goal_node + col_num_;
        break;
    case WalkMove::Direction::kRight:
        ++goal_node;
        r_goal_node = goal_node + 1;
        break;
    case WalkMove::Direction::kBackward:
        goal_node -= col_num_;
        r_goal_node = goal_node - col_num_;
        break;
    case WalkMove::Direction::kLeft:
        --goal_node;
        r_goal_node = goal_node - 1;
        break;
    case WalkMove::Direction::kEnd:
    default:
        return -1;
    }

    if (is_possible_move(cur_node, goal_node)) {
        // the goal node is occupied by another pawn
        if (occ_nodes_.count(goal_node) > 0) {
            if (is_possible_move(goal_node, r_goal_node)) {
                goal_node = r_goal_node;
            }
            else {
                return -2;
            }
        }
    }
    else {
        return -1;
    }

    // update pawn's position
    occ_nodes_.erase(pawn_node_[pawn]);
    pawn_node_[pawn] = goal_node;
    occ_nodes_[goal_node] = pawn;

    return 0;
}

int Board::add_wall(const Wall &wall)
{
    int line_lim = (wall.orientation() ? col_num() : row_num()) - 1;
    int start_pos_lim = (wall.orientation() ? row_num() : col_num()) - 1;
    if ((wall.line() >= line_lim)
            || (wall.end_pos() >= start_pos_lim)) {
        return -1;
    }

    if (wall_intersects(wall)) {
        return -2;
    }

    walls_[wall.orientation()][wall.line()].insert(std::map<int, Wall>::value_type(wall.start_pos(), Wall(wall)));

    int node1;
    int node2;
    for (int i = 0; i < wall.cnt(); ++i) {
        if (wall.orientation() == 0) {
            node1 = wall.line() * row_num_ + wall.start_pos() + i;
            node2 = (wall.line() + 1) * row_num_ + wall.start_pos() + i;
        }
        else {
            node1 = (wall.start_pos() + i) * row_num_ + wall.line();
            node2 = (wall.start_pos() + i) * row_num_ + wall.line() + 1;
        }
        std::cout << "removing edge " << node1 << ":" << node2 << std::endl;
        bg_.remove_edges(node1, node2);
    }

    return 0;
}

bool Board::wall_intersects(const Wall &wall) const
{
    // check intersections
    if (walls_.count(1 - wall.orientation()) > 0) {
        for (int i = 0; i < wall.cnt() - 1; ++i) {
            // there are walls on the intersected line
            if (walls_.at(1 - wall.orientation()).count(wall.start_pos()) != 0) {
                auto line_walls = walls_.at(1 - wall.orientation()).at(wall.start_pos() + i);
                auto it = line_walls.upper_bound(wall.line());

                // all walls on the line are settled before new wall
                if (it == line_walls.end()) {
                    auto rit = line_walls.rbegin();
                    if (rit->second.end_pos() >= wall.line()) {
                        return true;
                    }
                }
                else if (it != line_walls.begin()) {
                    --it;
                    if (it->second.end_pos() >= wall.line()) {
                        return true;
                    }
                }
            }
        }
    }

    // check overlaps
    if (walls_.count(wall.orientation()) > 0) {
        if (walls_.at(wall.orientation()).count(wall.line()) != 0) {
            auto line_walls = walls_.at(wall.orientation()).at(wall.line());
            auto it = line_walls.upper_bound(wall.start_pos());

            // all walls on the line are settled before new wall
            if (it == line_walls.end()) {
                auto rit = line_walls.rbegin();
                if (rit->second.end_pos() >= wall.start_pos()) {
                    return true;
                }
            }
            else {
                // check if new wall overlaps over next wall on the line
                if (wall.end_pos() >= it->second.start_pos()) {
                    return true;
                }

                if (it != line_walls.begin()) {
                    --it;
                    if (it->second.end_pos() >= wall.start_pos()) {
                        return true;
                    }
                }
            }
        }
    }

    return false;
}

bool Board::is_possible_move(int cur_node, int goal_node) const
{
    return bg_.is_neighbours(cur_node, goal_node);
}

}  /* namespace Quoridor */
<|endoftext|>


<commit_before>#ifndef buffer_hh_INCLUDED
#define buffer_hh_INCLUDED

#include "coord.hh"
#include "flags.hh"
#include "safe_ptr.hh"
#include "scope.hh"
#include "shared_string.hh"
#include "value.hh"
#include "vector.hh"

namespace Kakoune
{

class Buffer;

constexpr timespec InvalidTime = { -1, -1 };

// A BufferIterator permits to iterate over the characters of a buffer
class BufferIterator
{
public:
    using value_type = char;
    using difference_type = size_t;
    using pointer = const value_type*;
    using reference = const value_type&;
    using iterator_category = std::random_access_iterator_tag;

    BufferIterator() : m_buffer(nullptr) {}
    BufferIterator(const Buffer& buffer, ByteCoord coord);

    bool operator== (const BufferIterator& iterator) const;
    bool operator!= (const BufferIterator& iterator) const;
    bool operator<  (const BufferIterator& iterator) const;
    bool operator<= (const BufferIterator& iterator) const;
    bool operator>  (const BufferIterator& iterator) const;
    bool operator>= (const BufferIterator& iterator) const;

    char   operator* () const;
    char   operator[](size_t n) const;
    size_t operator- (const BufferIterator& iterator) const;

    BufferIterator operator+ (ByteCount size) const;
    BufferIterator operator- (ByteCount size) const;

    BufferIterator& operator+= (ByteCount size);
    BufferIterator& operator-= (ByteCount size);

    BufferIterator& operator++ ();
    BufferIterator& operator-- ();

    BufferIterator operator++ (int);
    BufferIterator operator-- (int);

    const ByteCoord& coord() const { return m_coord; }

private:
    SafePtr<const Buffer> m_buffer;
    ByteCoord m_coord;
};

using BufferLines = Vector<StringDataPtr, MemoryDomain::BufferContent>;

// A Buffer is a in-memory representation of a file
//
// The Buffer class permits to read and mutate this file
// representation. It also manage modifications undo/redo and
// provides tools to deal with the line/column nature of text.
class Buffer : public SafeCountable, public OptionManagerWatcher, public Scope
{
public:
    enum class Flags
    {
        None = 0,
        File = 1,
        New  = 2,
        Fifo = 4,
        NoUndo = 8,
    };

    Buffer(String name, Flags flags, StringView data = {},
           timespec fs_timestamp = InvalidTime);
    Buffer(const Buffer&) = delete;
    Buffer& operator= (const Buffer&) = delete;
    ~Buffer();

    Flags flags() const { return m_flags; }
    Flags& flags() { return m_flags; }

    bool set_name(String name);

    BufferIterator insert(const BufferIterator& pos, StringView content);
    BufferIterator erase(BufferIterator begin, BufferIterator end);

    size_t         timestamp() const;
    timespec       fs_timestamp() const;
    void           set_fs_timestamp(timespec ts);

    void           commit_undo_group();
    bool           undo();
    bool           redo();

    String         string(ByteCoord begin, ByteCoord end) const;

    char           byte_at(ByteCoord c) const;
    ByteCount      distance(ByteCoord begin, ByteCoord end) const;
    ByteCoord      advance(ByteCoord coord, ByteCount count) const;
    ByteCoord      next(ByteCoord coord) const;
    ByteCoord      prev(ByteCoord coord) const;

    ByteCoord      char_next(ByteCoord coord) const;
    ByteCoord      char_prev(ByteCoord coord) const;

    ByteCoord      back_coord() const;
    ByteCoord      end_coord() const;

    bool           is_valid(ByteCoord c) const;
    bool           is_end(ByteCoord c) const;

    ByteCoord      last_modification_coord() const;

    BufferIterator begin() const;
    BufferIterator end() const;
    LineCount      line_count() const;

    StringView operator[](LineCount line) const
    { return m_lines[line]; }

    StringDataPtr line_storage(LineCount line) const
    { return m_lines.get_storage(line); }

    // returns an iterator at given coordinates. clamp line_and_column
    BufferIterator iterator_at(ByteCoord coord) const;

    // returns nearest valid coordinates from given ones
    ByteCoord clamp(ByteCoord coord) const;

    ByteCoord offset_coord(ByteCoord coord, CharCount offset);
    ByteCoordAndTarget offset_coord(ByteCoordAndTarget coord, LineCount offset);

    const String& name() const { return m_name; }
    const String& display_name() const { return m_display_name; }

    // returns true if the buffer is in a different state than
    // the last time it was saved
    bool is_modified() const;

    // notify the buffer that it was saved in the current state
    void notify_saved();

    ValueMap& values() const { return m_values; }

    void run_hook_in_own_context(StringView hook_name, StringView param);

    void reload(StringView data, timespec fs_timestamp = InvalidTime);

    void check_invariant() const;

    struct Change
    {
        enum Type : char { Insert, Erase };
        Type type;
        bool at_end;
        ByteCoord begin;
        ByteCoord end;
    };
    ConstArrayView<Change> changes_since(size_t timestamp) const;

    String debug_description() const;
private:

    void on_option_changed(const Option& option) override;

    ByteCoord do_insert(ByteCoord pos, StringView content);
    ByteCoord do_erase(ByteCoord begin, ByteCoord end);

    struct Modification;

    void apply_modification(const Modification& modification);
    void revert_modification(const Modification& modification);

    struct LineList : BufferLines
    {
        [[gnu::always_inline]]
        StringDataPtr& get_storage(LineCount line)
        { return BufferLines::operator[]((int)line); }

        [[gnu::always_inline]]
        const StringDataPtr& get_storage(LineCount line) const
        { return BufferLines::operator[]((int)line); }

        [[gnu::always_inline]]
        StringView operator[](LineCount line) const
        { return get_storage(line)->strview(); }

        StringView front() const { return BufferLines::front()->strview(); }
        StringView back() const { return BufferLines::back()->strview(); }
    };
    LineList m_lines;

    String m_name;
    String m_display_name;
    Flags  m_flags;

    using  UndoGroup = Vector<Modification, MemoryDomain::BufferMeta>;
    using  History = Vector<UndoGroup, MemoryDomain::BufferMeta>;

    History           m_history;
    History::iterator m_history_cursor;
    UndoGroup         m_current_undo_group;

    size_t m_last_save_undo_index;

    Vector<Change, MemoryDomain::BufferMeta> m_changes;

    timespec m_fs_timestamp;

    // Values are just data holding by the buffer, they are not part of its
    // observable state
    mutable ValueMap m_values;
};

template<> struct WithBitOps<Buffer::Flags> : std::true_type {};

}

#include "buffer.inl.hh"

#endif // buffer_hh_INCLUDED
<commit_msg>Do not tag BufferIterator as random access<commit_after>#ifndef buffer_hh_INCLUDED
#define buffer_hh_INCLUDED

#include "coord.hh"
#include "flags.hh"
#include "safe_ptr.hh"
#include "scope.hh"
#include "shared_string.hh"
#include "value.hh"
#include "vector.hh"

namespace Kakoune
{

class Buffer;

constexpr timespec InvalidTime = { -1, -1 };

// A BufferIterator permits to iterate over the characters of a buffer
class BufferIterator
{
public:
    using value_type = char;
    using difference_type = size_t;
    using pointer = const value_type*;
    using reference = const value_type&;
    using iterator_category = std::bidirectional_iterator_tag;

    BufferIterator() : m_buffer(nullptr) {}
    BufferIterator(const Buffer& buffer, ByteCoord coord);

    bool operator== (const BufferIterator& iterator) const;
    bool operator!= (const BufferIterator& iterator) const;
    bool operator<  (const BufferIterator& iterator) const;
    bool operator<= (const BufferIterator& iterator) const;
    bool operator>  (const BufferIterator& iterator) const;
    bool operator>= (const BufferIterator& iterator) const;

    char   operator* () const;
    char   operator[](size_t n) const;
    size_t operator- (const BufferIterator& iterator) const;

    BufferIterator operator+ (ByteCount size) const;
    BufferIterator operator- (ByteCount size) const;

    BufferIterator& operator+= (ByteCount size);
    BufferIterator& operator-= (ByteCount size);

    BufferIterator& operator++ ();
    BufferIterator& operator-- ();

    BufferIterator operator++ (int);
    BufferIterator operator-- (int);

    const ByteCoord& coord() const { return m_coord; }

private:
    SafePtr<const Buffer> m_buffer;
    ByteCoord m_coord;
};

using BufferLines = Vector<StringDataPtr, MemoryDomain::BufferContent>;

// A Buffer is a in-memory representation of a file
//
// The Buffer class permits to read and mutate this file
// representation. It also manage modifications undo/redo and
// provides tools to deal with the line/column nature of text.
class Buffer : public SafeCountable, public OptionManagerWatcher, public Scope
{
public:
    enum class Flags
    {
        None = 0,
        File = 1,
        New  = 2,
        Fifo = 4,
        NoUndo = 8,
    };

    Buffer(String name, Flags flags, StringView data = {},
           timespec fs_timestamp = InvalidTime);
    Buffer(const Buffer&) = delete;
    Buffer& operator= (const Buffer&) = delete;
    ~Buffer();

    Flags flags() const { return m_flags; }
    Flags& flags() { return m_flags; }

    bool set_name(String name);

    BufferIterator insert(const BufferIterator& pos, StringView content);
    BufferIterator erase(BufferIterator begin, BufferIterator end);

    size_t         timestamp() const;
    timespec       fs_timestamp() const;
    void           set_fs_timestamp(timespec ts);

    void           commit_undo_group();
    bool           undo();
    bool           redo();

    String         string(ByteCoord begin, ByteCoord end) const;

    char           byte_at(ByteCoord c) const;
    ByteCount      distance(ByteCoord begin, ByteCoord end) const;
    ByteCoord      advance(ByteCoord coord, ByteCount count) const;
    ByteCoord      next(ByteCoord coord) const;
    ByteCoord      prev(ByteCoord coord) const;

    ByteCoord      char_next(ByteCoord coord) const;
    ByteCoord      char_prev(ByteCoord coord) const;

    ByteCoord      back_coord() const;
    ByteCoord      end_coord() const;

    bool           is_valid(ByteCoord c) const;
    bool           is_end(ByteCoord c) const;

    ByteCoord      last_modification_coord() const;

    BufferIterator begin() const;
    BufferIterator end() const;
    LineCount      line_count() const;

    StringView operator[](LineCount line) const
    { return m_lines[line]; }

    StringDataPtr line_storage(LineCount line) const
    { return m_lines.get_storage(line); }

    // returns an iterator at given coordinates. clamp line_and_column
    BufferIterator iterator_at(ByteCoord coord) const;

    // returns nearest valid coordinates from given ones
    ByteCoord clamp(ByteCoord coord) const;

    ByteCoord offset_coord(ByteCoord coord, CharCount offset);
    ByteCoordAndTarget offset_coord(ByteCoordAndTarget coord, LineCount offset);

    const String& name() const { return m_name; }
    const String& display_name() const { return m_display_name; }

    // returns true if the buffer is in a different state than
    // the last time it was saved
    bool is_modified() const;

    // notify the buffer that it was saved in the current state
    void notify_saved();

    ValueMap& values() const { return m_values; }

    void run_hook_in_own_context(StringView hook_name, StringView param);

    void reload(StringView data, timespec fs_timestamp = InvalidTime);

    void check_invariant() const;

    struct Change
    {
        enum Type : char { Insert, Erase };
        Type type;
        bool at_end;
        ByteCoord begin;
        ByteCoord end;
    };
    ConstArrayView<Change> changes_since(size_t timestamp) const;

    String debug_description() const;
private:

    void on_option_changed(const Option& option) override;

    ByteCoord do_insert(ByteCoord pos, StringView content);
    ByteCoord do_erase(ByteCoord begin, ByteCoord end);

    struct Modification;

    void apply_modification(const Modification& modification);
    void revert_modification(const Modification& modification);

    struct LineList : BufferLines
    {
        [[gnu::always_inline]]
        StringDataPtr& get_storage(LineCount line)
        { return BufferLines::operator[]((int)line); }

        [[gnu::always_inline]]
        const StringDataPtr& get_storage(LineCount line) const
        { return BufferLines::operator[]((int)line); }

        [[gnu::always_inline]]
        StringView operator[](LineCount line) const
        { return get_storage(line)->strview(); }

        StringView front() const { return BufferLines::front()->strview(); }
        StringView back() const { return BufferLines::back()->strview(); }
    };
    LineList m_lines;

    String m_name;
    String m_display_name;
    Flags  m_flags;

    using  UndoGroup = Vector<Modification, MemoryDomain::BufferMeta>;
    using  History = Vector<UndoGroup, MemoryDomain::BufferMeta>;

    History           m_history;
    History::iterator m_history_cursor;
    UndoGroup         m_current_undo_group;

    size_t m_last_save_undo_index;

    Vector<Change, MemoryDomain::BufferMeta> m_changes;

    timespec m_fs_timestamp;

    // Values are just data holding by the buffer, they are not part of its
    // observable state
    mutable ValueMap m_values;
};

template<> struct WithBitOps<Buffer::Flags> : std::true_type {};

}

#include "buffer.inl.hh"

#endif // buffer_hh_INCLUDED
<|endoftext|>


<commit_before>// Copyright 2015 Alessio Sclocco <a.sclocco@vu.nl>
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include <iostream>
#include <fstream>
#include <string>
#include <cstring>
#include <vector>
#include <iomanip>

#include <configuration.hpp>

#include <ArgumentList.hpp>
#include <Observation.hpp>
#include <ReadData.hpp>
#include <Shifts.hpp>
#include <Dedispersion.hpp>

int main(int argc, char * argv[]) {
  // Command line arguments
  isa::utils::ArgumentList args(argc, argv);
  // Observation
  AstroData::Observation observation;
  uint8_t inputBits = 0;
  // Files
	std::string dataFile;
	std::string headerFile;
	std::string outputFile;
  std::vector< std::ofstream > output;
  // LOFAR
  bool dataLOFAR = false;
  bool limit = false;
  // SIGPROC
  bool dataSIGPROC = false;
  unsigned int bytesToSkip = 0;
  // PSRDada
  bool dataPSRDada = false;
  key_t dadaKey;
  dada_hdu_t * ringBuffer;

  try {
    observation.setPadding(args.getSwitchArgument< unsigned int >("-padding"));
		if ( !((((!(dataLOFAR && dataSIGPROC) && dataPSRDada) || (!(dataLOFAR && dataPSRDada) && dataSIGPROC)) || (!(dataSIGPROC && dataPSRDada) && dataLOFAR)) || ((!dataLOFAR && !dataSIGPROC) && !dataPSRDada)) ) {
			std::cerr << "-lofar -sigproc and -dada are mutually exclusive." << std::endl;
			throw std::exception();
		} else if ( dataLOFAR ) {
      observation.setNrBeams(1);
			headerFile = args.getSwitchArgument< std::string >("-header");
			dataFile = args.getSwitchArgument< std::string >("-data");
      limit = args.getSwitch("-limit");
      if ( limit ) {
        observation.setNrSeconds(args.getSwitchArgument< unsigned int >("-seconds"));
      }
		} else if ( dataSIGPROC ) {
      observation.setNrBeams(1);
			bytesToSkip = args.getSwitchArgument< unsigned int >("-header");
			dataFile = args.getSwitchArgument< std::string >("-data");
			observation.setNrSeconds(args.getSwitchArgument< unsigned int >("-seconds"));
      observation.setFrequencyRange(args.getSwitchArgument< unsigned int >("-channels"), args.getSwitchArgument< float >("-min_freq"), args.getSwitchArgument< float >("-channel_bandwidth"));
			observation.setNrSamplesPerSecond(args.getSwitchArgument< unsigned int >("-samples"));
    } else if ( dataPSRDada ) {
      dadaKey = args.getSwitchArgument< key_t >("-dada_key");
      observation.setNrBeams(args.getSwitchArgument< unsigned int >("-beams"));
      observation.setNrSeconds(args.getSwitchArgument< unsigned int >("-seconds"));
		}
    inputBits = args.getSwitchArgument< unsigned int >("-input_bits");
		outputFile = args.getSwitchArgument< std::string >("-output");
    observation.setDMRange(1, args.getSwitchArgument< float >("-dm"), 0.0f);
	} catch ( isa::utils::EmptyCommandLine & err ) {
    std::cerr <<  args.getName() << " -padding ... [-lofar] [-sigproc] [-dada] -input_bits ... -output ... -dm ..." << std::endl;
    std::cerr << "\t -lofar -header ... -data ... [-limit]" << std::endl;
    std::cerr << "\t\t -limit -seconds ..." << std::endl;
    std::cerr << "\t -sigproc -header ... -data ... -seconds ... -channels ... -min_freq ... -channel_bandwidth ... -samples ..." << std::endl;
    std::cerr << "\t -dada -dada_key ... -beams ... -seconds ..." << std::endl;
    return 1;
  } catch ( std::exception & err ) {
		std::cerr << err.what() << std::endl;
		return 1;
	}

	// Load observation data
  std::vector< std::vector< std::vector< inputDataType > * > * > input(observation.getNrBeams());
	if ( dataLOFAR ) {
    input[0] = new std::vector< std::vector< inputDataType > * >(observation.getNrSeconds());
    if ( limit ) {
      AstroData::readLOFAR(headerFile, dataFile, obs, *(input[0]), observation.getNrSeconds());
    } else {
      AstroData::readLOFAR(headerFile, dataFile, obs, *(input[0]));
    }
	} else if ( dataSIGPROC ) {
    input[0] = new std::vector< std::vector< inputDataType > * >(observation.getNrSeconds());
    AstroData::readSIGPROC(obs, inputBits, bytesToSkip, dataFile, *(input[0]));
  } else if ( dataPSRDada ) {
    ringBuffer = dada_hdu_create(0);
    dada_hdu_set_key(ringBuffer, dadaKey);
    dada_hdu_connect(ringBuffer);
    dada_hdu_lock_read(ringBuffer);
	}
  output = std::vector< std::ofstream >(observation.getNrBeams());
  std::cout << "Padding: " << observation.getPadding() << std::endl;
  std::cout << std::endl;
  std::cout << "Beams: " << observation.getNrBeams() << std::endl;
  std::cout << "Seconds: " << observation.getNrSeconds() << std::endl;
  std::cout << "Samples: " << observation.getNrSamplesPerSecond() << std::endl;
  std::cout << "Frequency range: " << observation.getMinFreq() << " MHz, " << observation.getMaxFreq() << " MHz" << std::endl;
  std::cout << "Channels: " << observation.getNrChannels() << " (" << observation.getChannelBandwidth() << " MHz)" << std::endl;
  std::cout << std::endl;

	// Host memory allocation
  std::vector< float > * shifts = PulsarSearch::getShifts(observation);
  observation.setNrSamplesPerDispersedChannel(observation.getNrSamplesPerSecond() + static_cast< unsigned int >(shifts->at(0) * observation.getFirstDM()));
  observation.setNrDelaySeconds(static_cast< unsigned int >(std::ceil(static_cast< double >(observation.getNrSamplesPerDispersedChannel()) / observation.getNrSamplesPerSecond())));
  std::vector< std::vector< inputDataType > > dispersedData(observation.getNrBeams());
  std::vector< std::vector< outputDataType > > dedispersedData(observation.getNrBeams());

  for ( unsigned int beam = 0; beam < observation.getNrBeams(); beam++ ) {
    if ( inputBits >= 8 ) {
      dispersedData[beam] = std::vector< inputDataType >(observation.getNrChannels() * observation.getNrSamplesPerPaddedDispersedChannel());
    } else {
      dispersedData[beam] = std::vector< inputDataType >(observation.getNrChannels() * isa::utils::pad(observation.getNrSamplesPerDispersedChannel() / (8 / inputBits), observation.getPadding()));
    }
    dedispersedData[beam] = std::vector< outputDataType >(observation.getNrSamplesPerPaddedSecond());
  }

  // Open output files
  for ( unsigned int beam = 0; beam < observation.getNrBeams(); beam++ ) {
    output[beam].open(outputFile + "_B" + isa::utils::toString(beam) + ".tim");
    output[beam] << std::fixed << std::setprecision(6);
  }

  // Dedispersion loop
  for ( unsigned int second = 0; second < observation.getNrSeconds() - observation.getNrDelaySeconds(); second++ ) {
    for ( unsigned int beam = 0; beam < observation.getNrBeams(); beam++ ) {
      for ( unsigned int channel = 0; channel < observation.getNrChannels(); channel++ ) {
        for ( unsigned int chunk = 0; chunk < observation.getNrDelaySeconds() - 1; chunk++ ) {
          if ( inputBits >= 8 ) {
            std::memcpy(reinterpret_cast< void * >(&(dispersedData[beam].data()[(channel * observation.getNrSamplesPerPaddedDispersedChannel()) + (chunk * observation.getNrSamplesPerSecond())])), reinterpret_cast< void * >(&((input[beam]->at(second + chunk))->at(channel * observation.getNrSamplesPerPaddedSecond()))), observation.getNrSamplesPerSecond() * sizeof(inputDataType));
          } else {
            std::memcpy(reinterpret_cast< void * >(&(dispersedData[beam].data()[(channel * isa::utils::pad(observation.getNrSamplesPerDispersedChannel() / (8 / inputBits), observation.getPadding())) + (chunk * (observation.getNrSamplesPerSecond() / (8 / inputBits)))])), reinterpret_cast< void * >(&((input[beam]->at(second + chunk))->at(channel * isa::utils::pad(observation.getNrSamplesPerSecond() / (8 / inputBits), observation.getPadding())))), (observation.getNrSamplesPerSecond() / (8 / inputBits)) * sizeof(inputDataType));
          }
        }
        if ( observation.getNrSamplesPerDispersedChannel() % observation.getNrSamplesPerSecond() == 0 ) {
          if ( inputBits >= 8 ) {
            std::memcpy(reinterpret_cast< void * >(&(dispersedData[beam].data()[(channel * observation.getNrSamplesPerPaddedDispersedChannel()) + ((observation.getNrDelaySeconds() - 1) * observation.getNrSamplesPerSecond())])), reinterpret_cast< void * >(&((input[beam]->at(second + (observation.getNrDelaySeconds() - 1)))->at(channel * observation.getNrSamplesPerPaddedSecond()))), observation.getNrSamplesPerDispersedChannel() * sizeof(inputDataType));
          } else {
            std::memcpy(reinterpret_cast< void * >(&(dispersedData[beam].data()[(channel * isa::utils::pad(observation.getNrSamplesPerDispersedChannel() / (8 / inputBits), observation.getPadding())) + ((observation.getNrDelaySeconds() - 1) * (observation.getNrSamplesPerSecond() / (8 / inputBits)))])), reinterpret_cast< void * >(&((input[beam]->at(second + (observation.getNrDelaySeconds() - 1)))->at(channel * isa::utils::pad(observation.getNrSamplesPerSecond() / (8 / inputBits), observation.getPadding())))), (observation.getNrSamplesPerDispersedChannel() / (8 / inputBits)) * sizeof(inputDataType));
          }
        } else {
          if ( inputBits >= 8 ) {
            std::memcpy(reinterpret_cast< void * >(&(dispersedData[beam].data()[(channel * observation.getNrSamplesPerPaddedDispersedChannel()) + ((observation.getNrDelaySeconds() - 1) * observation.getNrSamplesPerSecond())])), reinterpret_cast< void * >(&((input[beam]->at(second + (observation.getNrDelaySeconds() - 1)))->at(channel * observation.getNrSamplesPerPaddedSecond()))), (observation.getNrSamplesPerDispersedChannel() % observation.getNrSamplesPerSecond()) * sizeof(inputDataType));
          } else {
            std::memcpy(reinterpret_cast< void * >(&(dispersedData[beam].data()[(channel * isa::utils::pad(observation.getNrSamplesPerDispersedChannel() / (8 / inputBits), observation.getPadding())) + ((observation.getNrDelaySeconds() - 1) * (observation.getNrSamplesPerSecond() / (8 / inputBits)))])), reinterpret_cast< void * >(&((input[beam]->at(second + (observation.getNrDelaySeconds() - 1)))->at(channel * isa::utils::pad(observation.getNrSamplesPerSecond() / (8 / inputBits), observation.getPadding())))), ((observation.getNrSamplesPerDispersedChannel() % observation.getNrSamplesPerSecond()) / (8 / inputBits)) * sizeof(inputDataType));
          }
        }
      }
      PulsarSearch::dedispersion< inputDataType, intermediateDataType, outputDataType >(observation, dispersedData[beam], dedispersedData[beam], *shifts, inputBits);
      for ( unsigned int sample = 0; sample < observation.getNrSamplesPerSecond(); sample++ ) {
        output[beam] << static_cast< uint64_t >((second * observation.getNrSamplesPerSecond()) + sample) * observation.getSamplingRate() << dedispersedData[beam][sample] << std::endl;
      }
    }
  }
  if ( dataPSRDada ) {
    dada_hdu_unlock_read(ringBuffer);
    dada_hdu_disconnect(ringBuffer);
  }

  // Close output files
  for ( unsigned int beam = 0; beam < observation.getNrBeams(); beam++ ) {
    output[beam].close();
  }

  return 0;
}

<commit_msg>Typo.<commit_after>// Copyright 2015 Alessio Sclocco <a.sclocco@vu.nl>
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include <iostream>
#include <fstream>
#include <string>
#include <cstring>
#include <vector>
#include <iomanip>

#include <configuration.hpp>

#include <ArgumentList.hpp>
#include <Observation.hpp>
#include <ReadData.hpp>
#include <Shifts.hpp>
#include <Dedispersion.hpp>

int main(int argc, char * argv[]) {
  // Command line arguments
  isa::utils::ArgumentList args(argc, argv);
  // Observation
  AstroData::Observation observation;
  uint8_t inputBits = 0;
  // Files
	std::string dataFile;
	std::string headerFile;
	std::string outputFile;
  std::vector< std::ofstream > output;
  // LOFAR
  bool dataLOFAR = false;
  bool limit = false;
  // SIGPROC
  bool dataSIGPROC = false;
  unsigned int bytesToSkip = 0;
  // PSRDada
  bool dataPSRDada = false;
  key_t dadaKey;
  dada_hdu_t * ringBuffer;

  try {
    observation.setPadding(args.getSwitchArgument< unsigned int >("-padding"));
		if ( !((((!(dataLOFAR && dataSIGPROC) && dataPSRDada) || (!(dataLOFAR && dataPSRDada) && dataSIGPROC)) || (!(dataSIGPROC && dataPSRDada) && dataLOFAR)) || ((!dataLOFAR && !dataSIGPROC) && !dataPSRDada)) ) {
			std::cerr << "-lofar -sigproc and -dada are mutually exclusive." << std::endl;
			throw std::exception();
		} else if ( dataLOFAR ) {
      observation.setNrBeams(1);
			headerFile = args.getSwitchArgument< std::string >("-header");
			dataFile = args.getSwitchArgument< std::string >("-data");
      limit = args.getSwitch("-limit");
      if ( limit ) {
        observation.setNrSeconds(args.getSwitchArgument< unsigned int >("-seconds"));
      }
		} else if ( dataSIGPROC ) {
      observation.setNrBeams(1);
			bytesToSkip = args.getSwitchArgument< unsigned int >("-header");
			dataFile = args.getSwitchArgument< std::string >("-data");
			observation.setNrSeconds(args.getSwitchArgument< unsigned int >("-seconds"));
      observation.setFrequencyRange(args.getSwitchArgument< unsigned int >("-channels"), args.getSwitchArgument< float >("-min_freq"), args.getSwitchArgument< float >("-channel_bandwidth"));
			observation.setNrSamplesPerSecond(args.getSwitchArgument< unsigned int >("-samples"));
    } else if ( dataPSRDada ) {
      dadaKey = args.getSwitchArgument< key_t >("-dada_key");
      observation.setNrBeams(args.getSwitchArgument< unsigned int >("-beams"));
      observation.setNrSeconds(args.getSwitchArgument< unsigned int >("-seconds"));
		}
    inputBits = args.getSwitchArgument< unsigned int >("-input_bits");
		outputFile = args.getSwitchArgument< std::string >("-output");
    observation.setDMRange(1, args.getSwitchArgument< float >("-dm"), 0.0f);
	} catch ( isa::utils::EmptyCommandLine & err ) {
    std::cerr <<  args.getName() << " -padding ... [-lofar] [-sigproc] [-dada] -input_bits ... -output ... -dm ..." << std::endl;
    std::cerr << "\t -lofar -header ... -data ... [-limit]" << std::endl;
    std::cerr << "\t\t -limit -seconds ..." << std::endl;
    std::cerr << "\t -sigproc -header ... -data ... -seconds ... -channels ... -min_freq ... -channel_bandwidth ... -samples ..." << std::endl;
    std::cerr << "\t -dada -dada_key ... -beams ... -seconds ..." << std::endl;
    return 1;
  } catch ( std::exception & err ) {
		std::cerr << err.what() << std::endl;
		return 1;
	}

	// Load observation data
  std::vector< std::vector< std::vector< inputDataType > * > * > input(observation.getNrBeams());
	if ( dataLOFAR ) {
    input[0] = new std::vector< std::vector< inputDataType > * >(observation.getNrSeconds());
    if ( limit ) {
      AstroData::readLOFAR(headerFile, dataFile, observation, *(input[0]), observation.getNrSeconds());
    } else {
      AstroData::readLOFAR(headerFile, dataFile, observation, *(input[0]));
    }
	} else if ( dataSIGPROC ) {
    input[0] = new std::vector< std::vector< inputDataType > * >(observation.getNrSeconds());
    AstroData::readSIGPROC(observation, inputBits, bytesToSkip, dataFile, *(input[0]));
  } else if ( dataPSRDada ) {
    ringBuffer = dada_hdu_create(0);
    dada_hdu_set_key(ringBuffer, dadaKey);
    dada_hdu_connect(ringBuffer);
    dada_hdu_lock_read(ringBuffer);
	}
  output = std::vector< std::ofstream >(observation.getNrBeams());
  std::cout << "Padding: " << observation.getPadding() << std::endl;
  std::cout << std::endl;
  std::cout << "Beams: " << observation.getNrBeams() << std::endl;
  std::cout << "Seconds: " << observation.getNrSeconds() << std::endl;
  std::cout << "Samples: " << observation.getNrSamplesPerSecond() << std::endl;
  std::cout << "Frequency range: " << observation.getMinFreq() << " MHz, " << observation.getMaxFreq() << " MHz" << std::endl;
  std::cout << "Channels: " << observation.getNrChannels() << " (" << observation.getChannelBandwidth() << " MHz)" << std::endl;
  std::cout << std::endl;

	// Host memory allocation
  std::vector< float > * shifts = PulsarSearch::getShifts(observation);
  observation.setNrSamplesPerDispersedChannel(observation.getNrSamplesPerSecond() + static_cast< unsigned int >(shifts->at(0) * observation.getFirstDM()));
  observation.setNrDelaySeconds(static_cast< unsigned int >(std::ceil(static_cast< double >(observation.getNrSamplesPerDispersedChannel()) / observation.getNrSamplesPerSecond())));
  std::vector< std::vector< inputDataType > > dispersedData(observation.getNrBeams());
  std::vector< std::vector< outputDataType > > dedispersedData(observation.getNrBeams());

  for ( unsigned int beam = 0; beam < observation.getNrBeams(); beam++ ) {
    if ( inputBits >= 8 ) {
      dispersedData[beam] = std::vector< inputDataType >(observation.getNrChannels() * observation.getNrSamplesPerPaddedDispersedChannel());
    } else {
      dispersedData[beam] = std::vector< inputDataType >(observation.getNrChannels() * isa::utils::pad(observation.getNrSamplesPerDispersedChannel() / (8 / inputBits), observation.getPadding()));
    }
    dedispersedData[beam] = std::vector< outputDataType >(observation.getNrSamplesPerPaddedSecond());
  }

  // Open output files
  for ( unsigned int beam = 0; beam < observation.getNrBeams(); beam++ ) {
    output[beam].open(outputFile + "_B" + isa::utils::toString(beam) + ".tim");
    output[beam] << std::fixed << std::setprecision(6);
  }

  // Dedispersion loop
  for ( unsigned int second = 0; second < observation.getNrSeconds() - observation.getNrDelaySeconds(); second++ ) {
    for ( unsigned int beam = 0; beam < observation.getNrBeams(); beam++ ) {
      for ( unsigned int channel = 0; channel < observation.getNrChannels(); channel++ ) {
        for ( unsigned int chunk = 0; chunk < observation.getNrDelaySeconds() - 1; chunk++ ) {
          if ( inputBits >= 8 ) {
            std::memcpy(reinterpret_cast< void * >(&(dispersedData[beam].data()[(channel * observation.getNrSamplesPerPaddedDispersedChannel()) + (chunk * observation.getNrSamplesPerSecond())])), reinterpret_cast< void * >(&((input[beam]->at(second + chunk))->at(channel * observation.getNrSamplesPerPaddedSecond()))), observation.getNrSamplesPerSecond() * sizeof(inputDataType));
          } else {
            std::memcpy(reinterpret_cast< void * >(&(dispersedData[beam].data()[(channel * isa::utils::pad(observation.getNrSamplesPerDispersedChannel() / (8 / inputBits), observation.getPadding())) + (chunk * (observation.getNrSamplesPerSecond() / (8 / inputBits)))])), reinterpret_cast< void * >(&((input[beam]->at(second + chunk))->at(channel * isa::utils::pad(observation.getNrSamplesPerSecond() / (8 / inputBits), observation.getPadding())))), (observation.getNrSamplesPerSecond() / (8 / inputBits)) * sizeof(inputDataType));
          }
        }
        if ( observation.getNrSamplesPerDispersedChannel() % observation.getNrSamplesPerSecond() == 0 ) {
          if ( inputBits >= 8 ) {
            std::memcpy(reinterpret_cast< void * >(&(dispersedData[beam].data()[(channel * observation.getNrSamplesPerPaddedDispersedChannel()) + ((observation.getNrDelaySeconds() - 1) * observation.getNrSamplesPerSecond())])), reinterpret_cast< void * >(&((input[beam]->at(second + (observation.getNrDelaySeconds() - 1)))->at(channel * observation.getNrSamplesPerPaddedSecond()))), observation.getNrSamplesPerDispersedChannel() * sizeof(inputDataType));
          } else {
            std::memcpy(reinterpret_cast< void * >(&(dispersedData[beam].data()[(channel * isa::utils::pad(observation.getNrSamplesPerDispersedChannel() / (8 / inputBits), observation.getPadding())) + ((observation.getNrDelaySeconds() - 1) * (observation.getNrSamplesPerSecond() / (8 / inputBits)))])), reinterpret_cast< void * >(&((input[beam]->at(second + (observation.getNrDelaySeconds() - 1)))->at(channel * isa::utils::pad(observation.getNrSamplesPerSecond() / (8 / inputBits), observation.getPadding())))), (observation.getNrSamplesPerDispersedChannel() / (8 / inputBits)) * sizeof(inputDataType));
          }
        } else {
          if ( inputBits >= 8 ) {
            std::memcpy(reinterpret_cast< void * >(&(dispersedData[beam].data()[(channel * observation.getNrSamplesPerPaddedDispersedChannel()) + ((observation.getNrDelaySeconds() - 1) * observation.getNrSamplesPerSecond())])), reinterpret_cast< void * >(&((input[beam]->at(second + (observation.getNrDelaySeconds() - 1)))->at(channel * observation.getNrSamplesPerPaddedSecond()))), (observation.getNrSamplesPerDispersedChannel() % observation.getNrSamplesPerSecond()) * sizeof(inputDataType));
          } else {
            std::memcpy(reinterpret_cast< void * >(&(dispersedData[beam].data()[(channel * isa::utils::pad(observation.getNrSamplesPerDispersedChannel() / (8 / inputBits), observation.getPadding())) + ((observation.getNrDelaySeconds() - 1) * (observation.getNrSamplesPerSecond() / (8 / inputBits)))])), reinterpret_cast< void * >(&((input[beam]->at(second + (observation.getNrDelaySeconds() - 1)))->at(channel * isa::utils::pad(observation.getNrSamplesPerSecond() / (8 / inputBits), observation.getPadding())))), ((observation.getNrSamplesPerDispersedChannel() % observation.getNrSamplesPerSecond()) / (8 / inputBits)) * sizeof(inputDataType));
          }
        }
      }
      PulsarSearch::dedispersion< inputDataType, intermediateDataType, outputDataType >(observation, dispersedData[beam], dedispersedData[beam], *shifts, inputBits);
      for ( unsigned int sample = 0; sample < observation.getNrSamplesPerSecond(); sample++ ) {
        output[beam] << static_cast< uint64_t >((second * observation.getNrSamplesPerSecond()) + sample) * observation.getSamplingRate() << dedispersedData[beam][sample] << std::endl;
      }
    }
  }
  if ( dataPSRDada ) {
    dada_hdu_unlock_read(ringBuffer);
    dada_hdu_disconnect(ringBuffer);
  }

  // Close output files
  for ( unsigned int beam = 0; beam < observation.getNrBeams(); beam++ ) {
    output[beam].close();
  }

  return 0;
}

<|endoftext|>


<commit_before>#include "client.hh"

#include "face_registry.hh"
#include "context.hh"
#include "buffer_manager.hh"
#include "buffer_utils.hh"
#include "file.hh"
#include "remote.hh"
#include "option.hh"
#include "client_manager.hh"
#include "command_manager.hh"
#include "event_manager.hh"
#include "user_interface.hh"
#include "window.hh"
#include "hash_map.hh"

#include <csignal>
#include <unistd.h>

#include <utility>

namespace Kakoune
{

Client::Client(std::unique_ptr<UserInterface>&& ui,
               std::unique_ptr<Window>&& window,
               SelectionList selections, int pid,
               EnvVarMap env_vars,
               String name,
               OnExitCallback on_exit)
    : m_ui{std::move(ui)}, m_window{std::move(window)},
      m_pid{pid},
      m_on_exit{std::move(on_exit)},
      m_input_handler{std::move(selections), Context::Flags::None,
                      std::move(name)},
      m_env_vars(std::move(env_vars))
{
    m_window->set_client(this);

    context().set_client(*this);
    context().set_window(*m_window);

    m_window->set_dimensions(m_ui->dimensions());
    m_window->options().register_watcher(*this);

    m_ui->set_ui_options(m_window->options()["ui_options"].get<UserInterface::Options>());
    m_ui->set_on_key([this](Key key) {
        if (key == ctrl('c'))
            killpg(getpgrp(), SIGINT);
        else
            m_pending_keys.push_back(key);
    });

    m_window->hooks().run_hook("WinDisplay", m_window->buffer().name(), context());

    force_redraw();
}

Client::~Client()
{
    m_window->options().unregister_watcher(*this);
    m_window->set_client(nullptr);
    // Do not move the selections here, as we need them to be valid
    // in order to correctly destroy the input handler
    ClientManager::instance().add_free_window(std::move(m_window),
                                              context().selections());
}

bool Client::process_pending_inputs()
{
    const bool debug_keys = (bool)(context().options()["debug"].get<DebugFlags>() & DebugFlags::Keys);
    // steal keys as we might receive new keys while handling them.
    Vector<Key, MemoryDomain::Client> keys = std::move(m_pending_keys);
    for (auto& key : keys)
    {
        try
        {
            if (debug_keys)
                write_to_debug_buffer(format("Client '{}' got key '{}'",
                                             context().name(), key_to_str(key)));

            if (key == Key::FocusIn)
                context().hooks().run_hook("FocusIn", context().name(), context());
            else if (key == Key::FocusOut)
                context().hooks().run_hook("FocusOut", context().name(), context());
            else if (key.modifiers == Key::Modifiers::Resize)
            {
                m_window->set_dimensions(m_ui->dimensions());
                force_redraw();
            }
            else
                m_input_handler.handle_key(key);

            context().hooks().run_hook("RawKey", key_to_str(key), context());
        }
        catch (Kakoune::runtime_error& error)
        {
            write_to_debug_buffer(format("Error: {}", error.what()));
            context().print_status({ fix_atom_text(error.what().str()), get_face("Error") });
            context().hooks().run_hook("RuntimeError", error.what(), context());
        }
    }
    return not keys.empty();
}

void Client::print_status(DisplayLine status_line, bool immediate)
{
    m_status_line = std::move(status_line);
    if (immediate)
    {
        m_ui->draw_status(m_status_line, m_mode_line, get_face("StatusLine"));
        m_ui->refresh(true);
    }
    else
    {
        m_ui_pending |= StatusLine;
    }
}

DisplayCoord Client::dimensions() const
{
    return m_ui->dimensions();
}

String generate_context_info(const Context& context)
{
    String s = "";
    if (context.buffer().is_modified())
        s += "[+]";
    if (context.client().input_handler().is_recording())
        s += format("[recording ({})]", context.client().input_handler().recording_reg());
    if (context.buffer().flags() & Buffer::Flags::New)
        s += "[new file]";
    if (context.hooks_disabled())
        s += "[no-hooks]";
    if (context.buffer().flags() & Buffer::Flags::Fifo)
        s += "[fifo]";
    if (context.buffer().flags() & Buffer::Flags::Debug)
        s += "[debug]";
    return s;
}

DisplayLine Client::generate_mode_line() const
{
    DisplayLine modeline;
    try
    {
        const String& modelinefmt = context().options()["modelinefmt"].get<String>();
        HashMap<String, DisplayLine> atoms{{ "mode_info", context().client().input_handler().mode_line() },
                                           { "context_info", {generate_context_info(context()), get_face("Information")}}};
        auto expanded = expand(modelinefmt, context(), ShellContext{},
                               [](String s) { return escape(s, '{', '\\'); });
        modeline = parse_display_line(expanded, atoms);
    }
    catch (runtime_error& err)
    {
        write_to_debug_buffer(format("Error while parsing modelinefmt: {}", err.what()));
        modeline.push_back({ "modelinefmt error, see *debug* buffer", get_face("Error") });
    }

    return modeline;
}

void Client::change_buffer(Buffer& buffer)
{
    if (m_buffer_reload_dialog_opened)
        close_buffer_reload_dialog();

    auto* current = &m_window->buffer();
    m_last_buffer = contains(BufferManager::instance(), current) ? current : nullptr;

    auto& client_manager = ClientManager::instance();
    m_window->options().unregister_watcher(*this);
    m_window->set_client(nullptr);
    client_manager.add_free_window(std::move(m_window),
                                   std::move(context().selections()));
    WindowAndSelections ws = client_manager.get_free_window(buffer);

    m_window = std::move(ws.window);
    m_window->set_client(this);
    m_window->options().register_watcher(*this);
    m_ui->set_ui_options(m_window->options()["ui_options"].get<UserInterface::Options>());

    context().selections_write_only() = std::move(ws.selections);
    context().set_window(*m_window);
    m_window->set_dimensions(m_ui->dimensions());

    m_window->hooks().run_hook("WinDisplay", buffer.name(), context());
    force_redraw();
}

static bool is_inline(InfoStyle style)
{
    return style == InfoStyle::Inline or
           style == InfoStyle::InlineAbove or
           style == InfoStyle::InlineBelow;
}

void Client::redraw_ifn()
{
    Window& window = context().window();
    if (window.needs_redraw(context()))
        m_ui_pending |= Draw;

    DisplayLine mode_line = generate_mode_line();
    if (mode_line.atoms() != m_mode_line.atoms())
    {
        m_ui_pending |= StatusLine;
        m_mode_line = std::move(mode_line);
    }

    if (m_ui_pending == 0)
        return;

    if (m_ui_pending & Draw)
        m_ui->draw(window.update_display_buffer(context()),
                   get_face("Default"), get_face("BufferPadding"));

    const bool update_menu_anchor = (m_ui_pending & Draw) and not (m_ui_pending & MenuHide) and
                                    not m_menu.items.empty() and m_menu.style == MenuStyle::Inline;
    if ((m_ui_pending & MenuShow) or update_menu_anchor)
    {
        auto anchor = m_menu.style == MenuStyle::Inline ?
            window.display_position(m_menu.anchor) : DisplayCoord{};
        if (not (m_ui_pending & MenuShow) and m_menu.ui_anchor != anchor)
            m_ui_pending |= anchor ? (MenuShow | MenuSelect) : MenuHide;
        m_menu.ui_anchor = anchor;
    }

    if (m_ui_pending & MenuShow and m_menu.ui_anchor)
        m_ui->menu_show(m_menu.items, *m_menu.ui_anchor,
                        get_face("MenuForeground"), get_face("MenuBackground"),
                        m_menu.style);
    if (m_ui_pending & MenuSelect and m_menu.ui_anchor)
        m_ui->menu_select(m_menu.selected);
    if (m_ui_pending & MenuHide)
        m_ui->menu_hide();

    const bool update_info_anchor = (m_ui_pending & Draw) and not (m_ui_pending & InfoHide) and
                                    not m_info.content.empty() and is_inline(m_info.style);
    if ((m_ui_pending & InfoShow) or update_info_anchor)
    {
        auto anchor = is_inline(m_info.style) ?
             window.display_position(m_info.anchor) : DisplayCoord{};
        if (not (m_ui_pending & MenuShow) and m_info.ui_anchor != anchor)
            m_ui_pending |= anchor ? InfoShow : InfoHide;
        m_info.ui_anchor = anchor;
    }

    if (m_ui_pending & InfoShow and m_info.ui_anchor)
        m_ui->info_show(m_info.title, m_info.content, *m_info.ui_anchor,
                        get_face("Information"), m_info.style);
    if (m_ui_pending & InfoHide)
        m_ui->info_hide();

    if (m_ui_pending & StatusLine)
        m_ui->draw_status(m_status_line, m_mode_line, get_face("StatusLine"));

    auto cursor = m_input_handler.get_cursor_info();
    m_ui->set_cursor(cursor.first, cursor.second);

    m_ui->refresh(m_ui_pending | Refresh);
    m_ui_pending = 0;
}

void Client::force_redraw()
{
    m_ui_pending |= Refresh | Draw | StatusLine |
        (m_menu.items.empty() ? MenuHide : MenuShow | MenuSelect) |
        (m_info.content.empty() ? InfoHide : InfoShow);
}

void Client::reload_buffer()
{
    Buffer& buffer = context().buffer();
    reload_file_buffer(buffer);
    context().print_status({ format("'{}' reloaded", buffer.display_name()),
                             get_face("Information") });
}

void Client::on_buffer_reload_key(Key key)
{
    auto& buffer = context().buffer();

    if (key == 'y' or key == Key::Return)
        reload_buffer();
    else if (key == 'n' or key == Key::Escape)
    {
        // reread timestamp in case the file was modified again
        buffer.set_fs_timestamp(get_fs_timestamp(buffer.name()));
        print_status({ format("'{}' kept", buffer.display_name()),
                       get_face("Information") });
    }
    else
    {
        print_status({ format("'{}' is not a valid choice", key_to_str(key)),
                       get_face("Error") });
        m_input_handler.on_next_key(KeymapMode::None, [this](Key key, Context&){ on_buffer_reload_key(key); });
        return;
    }

    for (auto& client : ClientManager::instance())
    {
        if (&client->context().buffer() == &buffer and
            client->m_buffer_reload_dialog_opened)
            client->close_buffer_reload_dialog();
    }
}

void Client::close_buffer_reload_dialog()
{
    kak_assert(m_buffer_reload_dialog_opened);
    // Reset first as this might check for reloading.
    m_input_handler.reset_normal_mode();
    m_buffer_reload_dialog_opened = false;
    info_hide(true);
}

void Client::check_if_buffer_needs_reloading()
{
    if (m_buffer_reload_dialog_opened)
        return;

    Buffer& buffer = context().buffer();
    auto reload = context().options()["autoreload"].get<Autoreload>();
    if (not (buffer.flags() & Buffer::Flags::File) or reload == Autoreload::No)
        return;

    const String& filename = buffer.name();
    timespec ts = get_fs_timestamp(filename);
    if (ts == InvalidTime or ts == buffer.fs_timestamp())
        return;
    if (reload == Autoreload::Ask)
    {
        StringView bufname = buffer.display_name();
        info_show(format("reload '{}' ?", bufname),
                  format("'{}' was modified externally\n"
                         "press <ret> or y to reload, <esc> or n to keep",
                         bufname), {}, InfoStyle::Modal);

        m_buffer_reload_dialog_opened = true;
        m_input_handler.on_next_key(KeymapMode::None, [this](Key key, Context&){ on_buffer_reload_key(key); });
    }
    else
        reload_buffer();
}

StringView Client::get_env_var(StringView name) const
{
    auto it = m_env_vars.find(name);
    if (it == m_env_vars.end())
        return {};
    return it->value;
}

void Client::on_option_changed(const Option& option)
{
    if (option.name() == "ui_options")
    {
        m_ui->set_ui_options(option.get<UserInterface::Options>());
        m_ui_pending |= Draw;
    }
}

void Client::menu_show(Vector<DisplayLine> choices, BufferCoord anchor, MenuStyle style)
{
    m_menu = Menu{ std::move(choices), anchor, {}, style, -1 };
    m_ui_pending |= MenuShow;
    m_ui_pending &= ~MenuHide;
}

void Client::menu_select(int selected)
{
    m_menu.selected = selected;
    m_ui_pending |= MenuSelect;
    m_ui_pending &= ~MenuHide;
}

void Client::menu_hide()
{
    m_menu = Menu{};
    m_ui_pending |= MenuHide;
    m_ui_pending &= ~(MenuShow | MenuSelect);
}

void Client::info_show(String title, String content, BufferCoord anchor, InfoStyle style)
{
    if (m_info.style == InfoStyle::Modal) // We already have a modal info opened, do not touch it.
        return;

    m_info = Info{ std::move(title), std::move(content), anchor, {}, style };
    m_ui_pending |= InfoShow;
    m_ui_pending &= ~InfoHide;
}

void Client::info_hide(bool even_modal)
{
    if (not even_modal and m_info.style == InfoStyle::Modal)
        return;

    m_info = Info{};
    m_ui_pending |= InfoHide;
    m_ui_pending &= ~InfoShow;
}

}
<commit_msg>Handle errors while reloading buffer gracefully<commit_after>#include "client.hh"

#include "face_registry.hh"
#include "context.hh"
#include "buffer_manager.hh"
#include "buffer_utils.hh"
#include "file.hh"
#include "remote.hh"
#include "option.hh"
#include "client_manager.hh"
#include "command_manager.hh"
#include "event_manager.hh"
#include "user_interface.hh"
#include "window.hh"
#include "hash_map.hh"

#include <csignal>
#include <unistd.h>

#include <utility>

namespace Kakoune
{

Client::Client(std::unique_ptr<UserInterface>&& ui,
               std::unique_ptr<Window>&& window,
               SelectionList selections, int pid,
               EnvVarMap env_vars,
               String name,
               OnExitCallback on_exit)
    : m_ui{std::move(ui)}, m_window{std::move(window)},
      m_pid{pid},
      m_on_exit{std::move(on_exit)},
      m_input_handler{std::move(selections), Context::Flags::None,
                      std::move(name)},
      m_env_vars(std::move(env_vars))
{
    m_window->set_client(this);

    context().set_client(*this);
    context().set_window(*m_window);

    m_window->set_dimensions(m_ui->dimensions());
    m_window->options().register_watcher(*this);

    m_ui->set_ui_options(m_window->options()["ui_options"].get<UserInterface::Options>());
    m_ui->set_on_key([this](Key key) {
        if (key == ctrl('c'))
            killpg(getpgrp(), SIGINT);
        else
            m_pending_keys.push_back(key);
    });

    m_window->hooks().run_hook("WinDisplay", m_window->buffer().name(), context());

    force_redraw();
}

Client::~Client()
{
    m_window->options().unregister_watcher(*this);
    m_window->set_client(nullptr);
    // Do not move the selections here, as we need them to be valid
    // in order to correctly destroy the input handler
    ClientManager::instance().add_free_window(std::move(m_window),
                                              context().selections());
}

bool Client::process_pending_inputs()
{
    const bool debug_keys = (bool)(context().options()["debug"].get<DebugFlags>() & DebugFlags::Keys);
    // steal keys as we might receive new keys while handling them.
    Vector<Key, MemoryDomain::Client> keys = std::move(m_pending_keys);
    for (auto& key : keys)
    {
        try
        {
            if (debug_keys)
                write_to_debug_buffer(format("Client '{}' got key '{}'",
                                             context().name(), key_to_str(key)));

            if (key == Key::FocusIn)
                context().hooks().run_hook("FocusIn", context().name(), context());
            else if (key == Key::FocusOut)
                context().hooks().run_hook("FocusOut", context().name(), context());
            else if (key.modifiers == Key::Modifiers::Resize)
            {
                m_window->set_dimensions(m_ui->dimensions());
                force_redraw();
            }
            else
                m_input_handler.handle_key(key);

            context().hooks().run_hook("RawKey", key_to_str(key), context());
        }
        catch (Kakoune::runtime_error& error)
        {
            write_to_debug_buffer(format("Error: {}", error.what()));
            context().print_status({ fix_atom_text(error.what().str()), get_face("Error") });
            context().hooks().run_hook("RuntimeError", error.what(), context());
        }
    }
    return not keys.empty();
}

void Client::print_status(DisplayLine status_line, bool immediate)
{
    m_status_line = std::move(status_line);
    if (immediate)
    {
        m_ui->draw_status(m_status_line, m_mode_line, get_face("StatusLine"));
        m_ui->refresh(true);
    }
    else
    {
        m_ui_pending |= StatusLine;
    }
}

DisplayCoord Client::dimensions() const
{
    return m_ui->dimensions();
}

String generate_context_info(const Context& context)
{
    String s = "";
    if (context.buffer().is_modified())
        s += "[+]";
    if (context.client().input_handler().is_recording())
        s += format("[recording ({})]", context.client().input_handler().recording_reg());
    if (context.buffer().flags() & Buffer::Flags::New)
        s += "[new file]";
    if (context.hooks_disabled())
        s += "[no-hooks]";
    if (context.buffer().flags() & Buffer::Flags::Fifo)
        s += "[fifo]";
    if (context.buffer().flags() & Buffer::Flags::Debug)
        s += "[debug]";
    return s;
}

DisplayLine Client::generate_mode_line() const
{
    DisplayLine modeline;
    try
    {
        const String& modelinefmt = context().options()["modelinefmt"].get<String>();
        HashMap<String, DisplayLine> atoms{{ "mode_info", context().client().input_handler().mode_line() },
                                           { "context_info", {generate_context_info(context()), get_face("Information")}}};
        auto expanded = expand(modelinefmt, context(), ShellContext{},
                               [](String s) { return escape(s, '{', '\\'); });
        modeline = parse_display_line(expanded, atoms);
    }
    catch (runtime_error& err)
    {
        write_to_debug_buffer(format("Error while parsing modelinefmt: {}", err.what()));
        modeline.push_back({ "modelinefmt error, see *debug* buffer", get_face("Error") });
    }

    return modeline;
}

void Client::change_buffer(Buffer& buffer)
{
    if (m_buffer_reload_dialog_opened)
        close_buffer_reload_dialog();

    auto* current = &m_window->buffer();
    m_last_buffer = contains(BufferManager::instance(), current) ? current : nullptr;

    auto& client_manager = ClientManager::instance();
    m_window->options().unregister_watcher(*this);
    m_window->set_client(nullptr);
    client_manager.add_free_window(std::move(m_window),
                                   std::move(context().selections()));
    WindowAndSelections ws = client_manager.get_free_window(buffer);

    m_window = std::move(ws.window);
    m_window->set_client(this);
    m_window->options().register_watcher(*this);
    m_ui->set_ui_options(m_window->options()["ui_options"].get<UserInterface::Options>());

    context().selections_write_only() = std::move(ws.selections);
    context().set_window(*m_window);
    m_window->set_dimensions(m_ui->dimensions());

    m_window->hooks().run_hook("WinDisplay", buffer.name(), context());
    force_redraw();
}

static bool is_inline(InfoStyle style)
{
    return style == InfoStyle::Inline or
           style == InfoStyle::InlineAbove or
           style == InfoStyle::InlineBelow;
}

void Client::redraw_ifn()
{
    Window& window = context().window();
    if (window.needs_redraw(context()))
        m_ui_pending |= Draw;

    DisplayLine mode_line = generate_mode_line();
    if (mode_line.atoms() != m_mode_line.atoms())
    {
        m_ui_pending |= StatusLine;
        m_mode_line = std::move(mode_line);
    }

    if (m_ui_pending == 0)
        return;

    if (m_ui_pending & Draw)
        m_ui->draw(window.update_display_buffer(context()),
                   get_face("Default"), get_face("BufferPadding"));

    const bool update_menu_anchor = (m_ui_pending & Draw) and not (m_ui_pending & MenuHide) and
                                    not m_menu.items.empty() and m_menu.style == MenuStyle::Inline;
    if ((m_ui_pending & MenuShow) or update_menu_anchor)
    {
        auto anchor = m_menu.style == MenuStyle::Inline ?
            window.display_position(m_menu.anchor) : DisplayCoord{};
        if (not (m_ui_pending & MenuShow) and m_menu.ui_anchor != anchor)
            m_ui_pending |= anchor ? (MenuShow | MenuSelect) : MenuHide;
        m_menu.ui_anchor = anchor;
    }

    if (m_ui_pending & MenuShow and m_menu.ui_anchor)
        m_ui->menu_show(m_menu.items, *m_menu.ui_anchor,
                        get_face("MenuForeground"), get_face("MenuBackground"),
                        m_menu.style);
    if (m_ui_pending & MenuSelect and m_menu.ui_anchor)
        m_ui->menu_select(m_menu.selected);
    if (m_ui_pending & MenuHide)
        m_ui->menu_hide();

    const bool update_info_anchor = (m_ui_pending & Draw) and not (m_ui_pending & InfoHide) and
                                    not m_info.content.empty() and is_inline(m_info.style);
    if ((m_ui_pending & InfoShow) or update_info_anchor)
    {
        auto anchor = is_inline(m_info.style) ?
             window.display_position(m_info.anchor) : DisplayCoord{};
        if (not (m_ui_pending & MenuShow) and m_info.ui_anchor != anchor)
            m_ui_pending |= anchor ? InfoShow : InfoHide;
        m_info.ui_anchor = anchor;
    }

    if (m_ui_pending & InfoShow and m_info.ui_anchor)
        m_ui->info_show(m_info.title, m_info.content, *m_info.ui_anchor,
                        get_face("Information"), m_info.style);
    if (m_ui_pending & InfoHide)
        m_ui->info_hide();

    if (m_ui_pending & StatusLine)
        m_ui->draw_status(m_status_line, m_mode_line, get_face("StatusLine"));

    auto cursor = m_input_handler.get_cursor_info();
    m_ui->set_cursor(cursor.first, cursor.second);

    m_ui->refresh(m_ui_pending | Refresh);
    m_ui_pending = 0;
}

void Client::force_redraw()
{
    m_ui_pending |= Refresh | Draw | StatusLine |
        (m_menu.items.empty() ? MenuHide : MenuShow | MenuSelect) |
        (m_info.content.empty() ? InfoHide : InfoShow);
}

void Client::reload_buffer()
{
    Buffer& buffer = context().buffer();
    try
    {
        reload_file_buffer(buffer);
        context().print_status({ format("'{}' reloaded", buffer.display_name()),
                                 get_face("Information") });
    }
    catch (runtime_error& error)
    {
        context().print_status({ format("error while reloading buffer: '{}'", error.what()),
                                 get_face("Error") });
        buffer.set_fs_timestamp(get_fs_timestamp(buffer.name()));
    }
}

void Client::on_buffer_reload_key(Key key)
{
    auto& buffer = context().buffer();

    if (key == 'y' or key == Key::Return)
        reload_buffer();
    else if (key == 'n' or key == Key::Escape)
    {
        // reread timestamp in case the file was modified again
        buffer.set_fs_timestamp(get_fs_timestamp(buffer.name()));
        print_status({ format("'{}' kept", buffer.display_name()),
                       get_face("Information") });
    }
    else
    {
        print_status({ format("'{}' is not a valid choice", key_to_str(key)),
                       get_face("Error") });
        m_input_handler.on_next_key(KeymapMode::None, [this](Key key, Context&){ on_buffer_reload_key(key); });
        return;
    }

    for (auto& client : ClientManager::instance())
    {
        if (&client->context().buffer() == &buffer and
            client->m_buffer_reload_dialog_opened)
            client->close_buffer_reload_dialog();
    }
}

void Client::close_buffer_reload_dialog()
{
    kak_assert(m_buffer_reload_dialog_opened);
    // Reset first as this might check for reloading.
    m_input_handler.reset_normal_mode();
    m_buffer_reload_dialog_opened = false;
    info_hide(true);
}

void Client::check_if_buffer_needs_reloading()
{
    if (m_buffer_reload_dialog_opened)
        return;

    Buffer& buffer = context().buffer();
    auto reload = context().options()["autoreload"].get<Autoreload>();
    if (not (buffer.flags() & Buffer::Flags::File) or reload == Autoreload::No)
        return;

    const String& filename = buffer.name();
    timespec ts = get_fs_timestamp(filename);
    if (ts == InvalidTime or ts == buffer.fs_timestamp())
        return;
    if (reload == Autoreload::Ask)
    {
        StringView bufname = buffer.display_name();
        info_show(format("reload '{}' ?", bufname),
                  format("'{}' was modified externally\n"
                         "press <ret> or y to reload, <esc> or n to keep",
                         bufname), {}, InfoStyle::Modal);

        m_buffer_reload_dialog_opened = true;
        m_input_handler.on_next_key(KeymapMode::None, [this](Key key, Context&){ on_buffer_reload_key(key); });
    }
    else
        reload_buffer();
}

StringView Client::get_env_var(StringView name) const
{
    auto it = m_env_vars.find(name);
    if (it == m_env_vars.end())
        return {};
    return it->value;
}

void Client::on_option_changed(const Option& option)
{
    if (option.name() == "ui_options")
    {
        m_ui->set_ui_options(option.get<UserInterface::Options>());
        m_ui_pending |= Draw;
    }
}

void Client::menu_show(Vector<DisplayLine> choices, BufferCoord anchor, MenuStyle style)
{
    m_menu = Menu{ std::move(choices), anchor, {}, style, -1 };
    m_ui_pending |= MenuShow;
    m_ui_pending &= ~MenuHide;
}

void Client::menu_select(int selected)
{
    m_menu.selected = selected;
    m_ui_pending |= MenuSelect;
    m_ui_pending &= ~MenuHide;
}

void Client::menu_hide()
{
    m_menu = Menu{};
    m_ui_pending |= MenuHide;
    m_ui_pending &= ~(MenuShow | MenuSelect);
}

void Client::info_show(String title, String content, BufferCoord anchor, InfoStyle style)
{
    if (m_info.style == InfoStyle::Modal) // We already have a modal info opened, do not touch it.
        return;

    m_info = Info{ std::move(title), std::move(content), anchor, {}, style };
    m_ui_pending |= InfoShow;
    m_ui_pending &= ~InfoHide;
}

void Client::info_hide(bool even_modal)
{
    if (not even_modal and m_info.style == InfoStyle::Modal)
        return;

    m_info = Info{};
    m_ui_pending |= InfoHide;
    m_ui_pending &= ~InfoShow;
}

}
<|endoftext|>


<commit_before>#include <cstring>
#include <iostream>
#include <iomanip>
#include <asio.hpp>
#include <asio/serial_port.hpp>
#include <asio/signal_set.hpp>
#include <google/protobuf/io/coded_stream.h>
#include "packet/frame.h"
#include "pose.pb.h"
#include "simulation.pb.h"

namespace {
    asio::io_service io_service;
    asio::serial_port port(io_service);
    constexpr size_t buffer_size = 1024;
    char rx_buffer[buffer_size];
    char sim_buffer[buffer_size];
    char frame_buffer[buffer_size];
    size_t frame_buffer_index = 0;
    SimulationMessage msg;

    void handle_read(const asio::error_code&, size_t);

    void start_read() {
        port.async_read_some(asio::buffer(rx_buffer, 512),
                std::bind(&handle_read,
                    std::placeholders::_1,
                    std::placeholders::_2));
    }

    void handle_read(const asio::error_code& error, size_t bytes_transferred) {
        if (error) {
            std::cerr << error.message() << "\n";
        } else {
            std::cout << "read " << bytes_transferred << " bytes" << std::endl;

            std::cout << std::hex;
            for (size_t i = 0; i < bytes_transferred; ++i) {
                std::cout << std::setw(2) << ((int)rx_buffer[i] & 0xff) << " ";
            }
            std::cout << std::dec << std::endl;

            size_t start_index = 0;
            size_t scan_index = 0;

            auto copy_to_frame_buffer = [&]() -> void {
                size_t length = scan_index - start_index;
                std::memcpy(frame_buffer + frame_buffer_index, rx_buffer + start_index, length);
                frame_buffer_index += length;
                start_index = ++scan_index;
            };

            auto decode_message = [&]() -> void {
                packet::frame::unstuff(frame_buffer, sim_buffer, frame_buffer_index);
                google::protobuf::io::CodedInputStream input(
                        (const uint8_t*)sim_buffer, frame_buffer_index - packet::frame::PACKET_FRAME_OVERHEAD);
                frame_buffer_index = 0;
                uint32_t size;
                if (!input.ReadVarint32(&size)) {
                    std::cerr << "Unable to decode packet size" << std::endl;
                    return;
                }
                if (msg.MergeFromCodedStream(&input)) {
                    msg.PrintDebugString();
                    msg.Clear();
                    std::cout << std::endl;
                }
            };

            while (scan_index < bytes_transferred) {
                if (rx_buffer[scan_index] == packet::frame::PACKET_DELIMITER) {
                    std::cout << "got packet delimiter at byte " << scan_index << std::endl;
                    copy_to_frame_buffer();
                    decode_message();
                } else {
                    ++scan_index;
                }
            }

            copy_to_frame_buffer();
        }
        start_read();
    }

    void handle_stop(const asio::error_code&, int signal_number) {
        if ((signal_number == SIGINT) || (signal_number == SIGTERM)) {
            io_service.stop();
        }
    }
} // namespace

int main(int argc, char* argv[]) {
    if (argc < 2) {
        std::cerr << "Usage: " << argv[0] << " <serial_device> <baud_rate>\n"
            << "\nTest serial communication.\n"
            << "A virtual serial port can be created using socat with:\n"
            << "\t$ socat -d -d pty,raw,echo=0 pty,raw,echo=0\n";
        return EXIT_FAILURE;
    }

    const std::string devname(argv[1]);
    uint32_t baud_rate = 115200;
    if (argc > 2) {
        baud_rate = std::atoi(argv[2]);
    }

    port.open(devname);
    port.set_option(asio::serial_port_base::baud_rate(baud_rate));
    port.set_option(asio::serial_port_base::parity(
                asio::serial_port_base::parity::none));
    port.set_option(asio::serial_port_base::character_size(8));
    port.set_option(asio::serial_port_base::flow_control(
                asio::serial_port_base::flow_control::none));
    port.set_option(asio::serial_port_base::stop_bits(
                asio::serial_port_base::stop_bits::one));

    asio::signal_set signals(io_service, SIGINT, SIGTERM);
    signals.async_wait(handle_stop);

    std::cout << "Decoding protobuf files from " << devname << ".\n";
    std::cout << "Press Ctrl-C to terminate.\n";

    start_read();
    io_service.run();
    return EXIT_SUCCESS;
}
<commit_msg>Fix decoding of messages with multiple sections<commit_after>#include <cstring>
#include <iostream>
#include <iomanip>
#include <asio.hpp>
#include <asio/serial_port.hpp>
#include <asio/signal_set.hpp>
#include <google/protobuf/io/coded_stream.h>
#include "packet/frame.h"
#include "pose.pb.h"
#include "simulation.pb.h"

namespace {
    asio::io_service io_service;
    asio::serial_port port(io_service);
    constexpr size_t buffer_size = 1024;
    char rx_buffer[buffer_size];
    char sim_buffer[buffer_size];
    char frame_buffer[buffer_size];
    size_t frame_buffer_index = 0;
    SimulationMessage msg;

    void handle_read(const asio::error_code&, size_t);

    void start_read() {
        port.async_read_some(asio::buffer(rx_buffer, 512),
                std::bind(&handle_read,
                    std::placeholders::_1,
                    std::placeholders::_2));
    }

    void handle_read(const asio::error_code& error, size_t bytes_transferred) {
        if (error) {
            std::cerr << error.message() << "\n";
        } else {
            std::cout << "read " << bytes_transferred << " bytes" << std::endl;

            std::cout << std::hex;
            for (size_t i = 0; i < bytes_transferred; ++i) {
                std::cout << std::setw(2) << ((int)rx_buffer[i] & 0xff) << " ";
            }
            std::cout << std::dec << std::endl;

            size_t start_index = 0;
            size_t scan_index = 0;

            auto copy_to_frame_buffer = [&]() -> void {
                size_t length = scan_index - start_index + 1; // delimiter included
                std::memcpy(frame_buffer + frame_buffer_index, rx_buffer + start_index, length);
                frame_buffer_index += length;
                start_index = ++scan_index;
            };

            auto decode_message = [&]() -> void {
                size_t unstuff_size = packet::frame::unstuff(frame_buffer, sim_buffer, frame_buffer_index);
                std::cout << "frame buffer index: " << frame_buffer_index << std::endl;
                std::cout << "unstuff size " << unstuff_size << std::endl;
                google::protobuf::io::CodedInputStream input(
                        (const uint8_t*)sim_buffer, frame_buffer_index);

                uint32_t size;
                if (!input.ReadVarint32(&size)) {
                    std::cerr << "Unable to decode packet size" << std::endl;
                    return;
                } else {
                    std::cout << "message size is " << size << std::endl;
                    if (frame_buffer_index < size) {
                        std::cout << "frame buffer index is smaller, skipping decode" << std::endl;
                        return;
                    }
                }
                if (msg.MergeFromCodedStream(&input)) {
                    msg.PrintDebugString();
                    msg.Clear();
                    frame_buffer_index = 0;
                    std::cout << std::endl;
                } else {
                    std::cout << "decode failed" << std::endl;
                }
            };

            while (scan_index < bytes_transferred) {
                if (rx_buffer[scan_index] == packet::frame::PACKET_DELIMITER) {
                    std::cout << "got packet delimiter at byte " << scan_index << std::endl;
                    copy_to_frame_buffer();
                    decode_message();
                } else {
                    ++scan_index;
                }
            }

            --scan_index;
            copy_to_frame_buffer();
        }
        start_read();
    }

    void handle_stop(const asio::error_code&, int signal_number) {
        if ((signal_number == SIGINT) || (signal_number == SIGTERM)) {
            io_service.stop();
        }
    }
} // namespace

int main(int argc, char* argv[]) {
    if (argc < 2) {
        std::cerr << "Usage: " << argv[0] << " <serial_device> <baud_rate>\n"
            << "\nTest serial communication.\n"
            << "A virtual serial port can be created using socat with:\n"
            << "\t$ socat -d -d pty,raw,echo=0 pty,raw,echo=0\n";
        return EXIT_FAILURE;
    }

    const std::string devname(argv[1]);
    uint32_t baud_rate = 115200;
    if (argc > 2) {
        baud_rate = std::atoi(argv[2]);
    }

    port.open(devname);
    port.set_option(asio::serial_port_base::baud_rate(baud_rate));
    port.set_option(asio::serial_port_base::parity(
                asio::serial_port_base::parity::none));
    port.set_option(asio::serial_port_base::character_size(8));
    port.set_option(asio::serial_port_base::flow_control(
                asio::serial_port_base::flow_control::none));
    port.set_option(asio::serial_port_base::stop_bits(
                asio::serial_port_base::stop_bits::one));

    asio::signal_set signals(io_service, SIGINT, SIGTERM);
    signals.async_wait(handle_stop);

    std::cout << "Decoding protobuf files from " << devname << ".\n";
    std::cout << "Press Ctrl-C to terminate.\n";

    start_read();
    io_service.run();
    return EXIT_SUCCESS;
}
<|endoftext|>