<commit_before>/*
 * Copyright (C) 2017 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 "partition_version.hh"
#include "flat_mutation_reader.hh"

struct partition_snapshot_reader_dummy_accounter {
   void operator()(const clustering_row& cr) {}
   void operator()(const static_row& sr) {}
   void operator()(const range_tombstone& rt) {}
   void operator()(const partition_start& ph) {}
   void operator()(const partition_end& eop) {}
};
extern partition_snapshot_reader_dummy_accounter no_accounter;

inline void maybe_merge_versions(lw_shared_ptr<partition_snapshot>& snp,
                                 logalloc::region& lsa_region,
                                 logalloc::allocating_section& read_section) {
    if (!snp.owned()) {
        return;
    }
    // If no one else is using this particular snapshot try to merge partition
    // versions.
    with_allocator(lsa_region.allocator(), [&snp, &lsa_region, &read_section] {
        return with_linearized_managed_bytes([&snp, &lsa_region, &read_section] {
            try {
                // Allocating sections require the region to be reclaimable
                // which means that they cannot be nested.
                // It is, however, possible, that if the snapshot is taken
                // inside an allocating section and then an exception is thrown
                // this function will be called to clean up even though we
                // still will be in the context of the allocating section.
                if (lsa_region.reclaiming_enabled()) {
                    read_section(lsa_region, [&snp] {
                        snp->merge_partition_versions();
                    });
                }
            } catch (...) { }
            snp = {};
        });
    });
}

template <typename MemoryAccounter = partition_snapshot_reader_dummy_accounter>
class partition_snapshot_flat_reader : public flat_mutation_reader::impl, public MemoryAccounter {
    struct rows_position {
        mutation_partition::rows_type::const_iterator _position;
        mutation_partition::rows_type::const_iterator _end;
    };

    class heap_compare {
        rows_entry::compare _cmp;
    public:
        explicit heap_compare(const schema& s) : _cmp(s) { }
        bool operator()(const rows_position& a, const rows_position& b) {
            return _cmp(*b._position, *a._position);
        }
    };
private:
    // Keeps shared pointer to the container we read mutation from to make sure
    // that its lifetime is appropriately extended.
    boost::any _container_guard;

    query::clustering_key_filter_ranges _ck_ranges;
    query::clustering_row_ranges::const_iterator _current_ck_range;
    query::clustering_row_ranges::const_iterator _ck_range_end;
    bool _in_ck_range = false;

    rows_entry::compare _cmp;
    position_in_partition::equal_compare _eq;
    heap_compare _heap_cmp;

    lw_shared_ptr<partition_snapshot> _snapshot;
    stdx::optional<position_in_partition> _last_entry;

    std::vector<rows_position> _clustering_rows;

    range_tombstone_stream _range_tombstones;

    logalloc::region& _lsa_region;
    logalloc::allocating_section& _read_section;

    MemoryAccounter& mem_accounter() {
        return *this;
    }

    partition_snapshot::change_mark _change_mark;
private:
    void refresh_iterators() {
        _clustering_rows.clear();

        if (!_in_ck_range) {
            if (_current_ck_range == _ck_range_end) {
                _end_of_stream = true;
                return;
            }
            for (auto&& v : _snapshot->versions()) {
                _range_tombstones.apply(v.partition().row_tombstones(), *_current_ck_range);
            }
        }

        for (auto&& v : _snapshot->versions()) {
            auto cr_end = v.partition().upper_bound(*_schema, *_current_ck_range);
            auto cr = [&] () -> mutation_partition::rows_type::const_iterator {
                if (_in_ck_range) {
                    return v.partition().clustered_rows().upper_bound(*_last_entry, _cmp);
                } else {
                    return v.partition().lower_bound(*_schema, *_current_ck_range);
                }
            }();

            if (cr != cr_end) {
                _clustering_rows.emplace_back(rows_position { cr, cr_end });
            }
        }

        _in_ck_range = true;
        boost::range::make_heap(_clustering_rows, _heap_cmp);
    }

    // Valid if has_more_rows()
    const rows_entry& pop_clustering_row() {
        boost::range::pop_heap(_clustering_rows, _heap_cmp);
        auto& current = _clustering_rows.back();
        const rows_entry& e = *current._position;
        current._position = std::next(current._position);
        if (current._position == current._end) {
            _clustering_rows.pop_back();
        } else {
            boost::range::push_heap(_clustering_rows, _heap_cmp);
        }
        return e;
    }

    // Valid if has_more_rows()
    const rows_entry& peek_row() const {
        return *_clustering_rows.front()._position;
    }

    bool has_more_rows() const {
        return !_clustering_rows.empty();
    }

    mutation_fragment_opt read_static_row() {
        _last_entry = position_in_partition(position_in_partition::static_row_tag_t());
        return mutation_fragment(_snapshot->static_row());
    }

    mutation_fragment_opt read_next() {
        while (has_more_rows()) {
            auto mf = _range_tombstones.get_next(peek_row());
            if (mf) {
                return mf;
            }
            const rows_entry& e = pop_clustering_row();
            if (e.dummy()) {
                continue;
            }
            clustering_row result = e;
            while (has_more_rows() && _eq(peek_row().position(), result.position())) {
                result.apply(*_schema, pop_clustering_row());
            }
            _last_entry = position_in_partition(result.position());
            return mutation_fragment(std::move(result));
        }
        return _range_tombstones.get_next(position_in_partition_view::for_range_end(*_current_ck_range));
    }

    void emplace_mutation_fragment(mutation_fragment&& mfopt) {
        mfopt.visit(mem_accounter());
        push_mutation_fragment(std::move(mfopt));
    }

    void do_fill_buffer() {
        if (!_last_entry) {
            auto mfopt = read_static_row();
            if (mfopt) {
                emplace_mutation_fragment(std::move(*mfopt));
            }
        }

        auto mark = _snapshot->get_change_mark();
        if (!_in_ck_range || mark != _change_mark) {
            refresh_iterators();
            _change_mark = mark;
        }

        while (!is_end_of_stream() && !is_buffer_full()) {
            auto mfopt = read_next();
            if (mfopt) {
                emplace_mutation_fragment(std::move(*mfopt));
            } else {
                _in_ck_range = false;
                _current_ck_range = std::next(_current_ck_range);
                refresh_iterators();
            }
        }
        if (is_end_of_stream()) {
            push_mutation_fragment(partition_end());
        }
    }

    static tombstone tomb(partition_snapshot& snp) {
        tombstone t;
        for (auto& v : snp.versions()) {
            t.apply(v.partition().partition_tombstone());
        }
        return t;
    }
public:
    template <typename... Args>
    partition_snapshot_flat_reader(schema_ptr s, dht::decorated_key dk, lw_shared_ptr<partition_snapshot> snp,
                              query::clustering_key_filter_ranges crr,
                              logalloc::region& region, logalloc::allocating_section& read_section,
                              boost::any pointer_to_container, Args&&... args)
        : impl(std::move(s))
        , MemoryAccounter(std::forward<Args>(args)...)
        , _container_guard(std::move(pointer_to_container))
        , _ck_ranges(std::move(crr))
        , _current_ck_range(_ck_ranges.begin())
        , _ck_range_end(_ck_ranges.end())
        , _cmp(*_schema)
        , _eq(*_schema)
        , _heap_cmp(*_schema)
        , _snapshot(snp)
        , _range_tombstones(*_schema)
        , _lsa_region(region)
        , _read_section(read_section) {
        push_mutation_fragment(partition_start(std::move(dk), tomb(*snp)));
        do_fill_buffer();
    }

    ~partition_snapshot_flat_reader() {
        maybe_merge_versions(_snapshot, _lsa_region, _read_section);
    }

    virtual future<> fill_buffer() override {
        return _read_section(_lsa_region, [&] {
            return with_linearized_managed_bytes([&] {
                do_fill_buffer();
                return make_ready_future<>();
            });
        });
    }
    virtual void next_partition() override {
        clear_buffer_to_next_partition();
        if (is_buffer_empty()) {
            _end_of_stream = true;
        }
    }
    virtual future<> fast_forward_to(const dht::partition_range& pr) override {
        throw std::runtime_error("This reader can't be fast forwarded to another partition.");
    };
    virtual future<> fast_forward_to(position_range cr) override {
        throw std::runtime_error("This reader can't be fast forwarded to another position.");
    };
};

template <typename MemoryAccounter, typename... Args>
inline flat_mutation_reader
make_partition_snapshot_flat_reader(schema_ptr s,
                                    dht::decorated_key dk,
                                    query::clustering_key_filter_ranges crr,
                                    lw_shared_ptr<partition_snapshot> snp,
                                    logalloc::region& region,
                                    logalloc::allocating_section& read_section,
                                    boost::any pointer_to_container,
                                    streamed_mutation::forwarding fwd,
                                    Args&&... args)
{
    auto res = make_flat_mutation_reader<partition_snapshot_flat_reader<MemoryAccounter>>(std::move(s), std::move(dk),
            snp, std::move(crr), region, read_section, std::move(pointer_to_container), std::forward<Args>(args)...);
    if (fwd) {
        return make_forwardable(std::move(res)); // FIXME: optimize
    } else {
        return std::move(res);
    }
}

inline flat_mutation_reader
make_partition_snapshot_flat_reader(schema_ptr s,
                                    dht::decorated_key dk,
                                    query::clustering_key_filter_ranges crr,
                                    lw_shared_ptr<partition_snapshot> snp,
                                    logalloc::region& region,
                                    logalloc::allocating_section& read_section,
                                    boost::any pointer_to_container,
                                    streamed_mutation::forwarding fwd)
{
    return make_partition_snapshot_flat_reader<partition_snapshot_reader_dummy_accounter>(std::move(s),
            std::move(dk), std::move(crr), std::move(snp), region, read_section, std::move(pointer_to_container), fwd);
}
<commit_msg>partition_snapshot_reader: Don't push empty static rows<commit_after>/*
 * Copyright (C) 2017 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 "partition_version.hh"
#include "flat_mutation_reader.hh"

struct partition_snapshot_reader_dummy_accounter {
   void operator()(const clustering_row& cr) {}
   void operator()(const static_row& sr) {}
   void operator()(const range_tombstone& rt) {}
   void operator()(const partition_start& ph) {}
   void operator()(const partition_end& eop) {}
};
extern partition_snapshot_reader_dummy_accounter no_accounter;

inline void maybe_merge_versions(lw_shared_ptr<partition_snapshot>& snp,
                                 logalloc::region& lsa_region,
                                 logalloc::allocating_section& read_section) {
    if (!snp.owned()) {
        return;
    }
    // If no one else is using this particular snapshot try to merge partition
    // versions.
    with_allocator(lsa_region.allocator(), [&snp, &lsa_region, &read_section] {
        return with_linearized_managed_bytes([&snp, &lsa_region, &read_section] {
            try {
                // Allocating sections require the region to be reclaimable
                // which means that they cannot be nested.
                // It is, however, possible, that if the snapshot is taken
                // inside an allocating section and then an exception is thrown
                // this function will be called to clean up even though we
                // still will be in the context of the allocating section.
                if (lsa_region.reclaiming_enabled()) {
                    read_section(lsa_region, [&snp] {
                        snp->merge_partition_versions();
                    });
                }
            } catch (...) { }
            snp = {};
        });
    });
}

template <typename MemoryAccounter = partition_snapshot_reader_dummy_accounter>
class partition_snapshot_flat_reader : public flat_mutation_reader::impl, public MemoryAccounter {
    struct rows_position {
        mutation_partition::rows_type::const_iterator _position;
        mutation_partition::rows_type::const_iterator _end;
    };

    class heap_compare {
        rows_entry::compare _cmp;
    public:
        explicit heap_compare(const schema& s) : _cmp(s) { }
        bool operator()(const rows_position& a, const rows_position& b) {
            return _cmp(*b._position, *a._position);
        }
    };
private:
    // Keeps shared pointer to the container we read mutation from to make sure
    // that its lifetime is appropriately extended.
    boost::any _container_guard;

    query::clustering_key_filter_ranges _ck_ranges;
    query::clustering_row_ranges::const_iterator _current_ck_range;
    query::clustering_row_ranges::const_iterator _ck_range_end;
    bool _in_ck_range = false;

    rows_entry::compare _cmp;
    position_in_partition::equal_compare _eq;
    heap_compare _heap_cmp;

    lw_shared_ptr<partition_snapshot> _snapshot;
    stdx::optional<position_in_partition> _last_entry;

    std::vector<rows_position> _clustering_rows;

    range_tombstone_stream _range_tombstones;

    logalloc::region& _lsa_region;
    logalloc::allocating_section& _read_section;

    MemoryAccounter& mem_accounter() {
        return *this;
    }

    partition_snapshot::change_mark _change_mark;
private:
    void refresh_iterators() {
        _clustering_rows.clear();

        if (!_in_ck_range) {
            if (_current_ck_range == _ck_range_end) {
                _end_of_stream = true;
                return;
            }
            for (auto&& v : _snapshot->versions()) {
                _range_tombstones.apply(v.partition().row_tombstones(), *_current_ck_range);
            }
        }

        for (auto&& v : _snapshot->versions()) {
            auto cr_end = v.partition().upper_bound(*_schema, *_current_ck_range);
            auto cr = [&] () -> mutation_partition::rows_type::const_iterator {
                if (_in_ck_range) {
                    return v.partition().clustered_rows().upper_bound(*_last_entry, _cmp);
                } else {
                    return v.partition().lower_bound(*_schema, *_current_ck_range);
                }
            }();

            if (cr != cr_end) {
                _clustering_rows.emplace_back(rows_position { cr, cr_end });
            }
        }

        _in_ck_range = true;
        boost::range::make_heap(_clustering_rows, _heap_cmp);
    }

    // Valid if has_more_rows()
    const rows_entry& pop_clustering_row() {
        boost::range::pop_heap(_clustering_rows, _heap_cmp);
        auto& current = _clustering_rows.back();
        const rows_entry& e = *current._position;
        current._position = std::next(current._position);
        if (current._position == current._end) {
            _clustering_rows.pop_back();
        } else {
            boost::range::push_heap(_clustering_rows, _heap_cmp);
        }
        return e;
    }

    // Valid if has_more_rows()
    const rows_entry& peek_row() const {
        return *_clustering_rows.front()._position;
    }

    bool has_more_rows() const {
        return !_clustering_rows.empty();
    }

    mutation_fragment_opt read_static_row() {
        _last_entry = position_in_partition(position_in_partition::static_row_tag_t());
        auto sr = _snapshot->static_row();
        if (!sr.empty()) {
            return mutation_fragment(std::move(sr));
        }
        return stdx::nullopt;
    }

    mutation_fragment_opt read_next() {
        while (has_more_rows()) {
            auto mf = _range_tombstones.get_next(peek_row());
            if (mf) {
                return mf;
            }
            const rows_entry& e = pop_clustering_row();
            if (e.dummy()) {
                continue;
            }
            clustering_row result = e;
            while (has_more_rows() && _eq(peek_row().position(), result.position())) {
                result.apply(*_schema, pop_clustering_row());
            }
            _last_entry = position_in_partition(result.position());
            return mutation_fragment(std::move(result));
        }
        return _range_tombstones.get_next(position_in_partition_view::for_range_end(*_current_ck_range));
    }

    void emplace_mutation_fragment(mutation_fragment&& mfopt) {
        mfopt.visit(mem_accounter());
        push_mutation_fragment(std::move(mfopt));
    }

    void do_fill_buffer() {
        if (!_last_entry) {
            auto mfopt = read_static_row();
            if (mfopt) {
                emplace_mutation_fragment(std::move(*mfopt));
            }
        }

        auto mark = _snapshot->get_change_mark();
        if (!_in_ck_range || mark != _change_mark) {
            refresh_iterators();
            _change_mark = mark;
        }

        while (!is_end_of_stream() && !is_buffer_full()) {
            auto mfopt = read_next();
            if (mfopt) {
                emplace_mutation_fragment(std::move(*mfopt));
            } else {
                _in_ck_range = false;
                _current_ck_range = std::next(_current_ck_range);
                refresh_iterators();
            }
        }
        if (is_end_of_stream()) {
            push_mutation_fragment(partition_end());
        }
    }

    static tombstone tomb(partition_snapshot& snp) {
        tombstone t;
        for (auto& v : snp.versions()) {
            t.apply(v.partition().partition_tombstone());
        }
        return t;
    }
public:
    template <typename... Args>
    partition_snapshot_flat_reader(schema_ptr s, dht::decorated_key dk, lw_shared_ptr<partition_snapshot> snp,
                              query::clustering_key_filter_ranges crr,
                              logalloc::region& region, logalloc::allocating_section& read_section,
                              boost::any pointer_to_container, Args&&... args)
        : impl(std::move(s))
        , MemoryAccounter(std::forward<Args>(args)...)
        , _container_guard(std::move(pointer_to_container))
        , _ck_ranges(std::move(crr))
        , _current_ck_range(_ck_ranges.begin())
        , _ck_range_end(_ck_ranges.end())
        , _cmp(*_schema)
        , _eq(*_schema)
        , _heap_cmp(*_schema)
        , _snapshot(snp)
        , _range_tombstones(*_schema)
        , _lsa_region(region)
        , _read_section(read_section) {
        push_mutation_fragment(partition_start(std::move(dk), tomb(*snp)));
        do_fill_buffer();
    }

    ~partition_snapshot_flat_reader() {
        maybe_merge_versions(_snapshot, _lsa_region, _read_section);
    }

    virtual future<> fill_buffer() override {
        return _read_section(_lsa_region, [&] {
            return with_linearized_managed_bytes([&] {
                do_fill_buffer();
                return make_ready_future<>();
            });
        });
    }
    virtual void next_partition() override {
        clear_buffer_to_next_partition();
        if (is_buffer_empty()) {
            _end_of_stream = true;
        }
    }
    virtual future<> fast_forward_to(const dht::partition_range& pr) override {
        throw std::runtime_error("This reader can't be fast forwarded to another partition.");
    };
    virtual future<> fast_forward_to(position_range cr) override {
        throw std::runtime_error("This reader can't be fast forwarded to another position.");
    };
};

template <typename MemoryAccounter, typename... Args>
inline flat_mutation_reader
make_partition_snapshot_flat_reader(schema_ptr s,
                                    dht::decorated_key dk,
                                    query::clustering_key_filter_ranges crr,
                                    lw_shared_ptr<partition_snapshot> snp,
                                    logalloc::region& region,
                                    logalloc::allocating_section& read_section,
                                    boost::any pointer_to_container,
                                    streamed_mutation::forwarding fwd,
                                    Args&&... args)
{
    auto res = make_flat_mutation_reader<partition_snapshot_flat_reader<MemoryAccounter>>(std::move(s), std::move(dk),
            snp, std::move(crr), region, read_section, std::move(pointer_to_container), std::forward<Args>(args)...);
    if (fwd) {
        return make_forwardable(std::move(res)); // FIXME: optimize
    } else {
        return std::move(res);
    }
}

inline flat_mutation_reader
make_partition_snapshot_flat_reader(schema_ptr s,
                                    dht::decorated_key dk,
                                    query::clustering_key_filter_ranges crr,
                                    lw_shared_ptr<partition_snapshot> snp,
                                    logalloc::region& region,
                                    logalloc::allocating_section& read_section,
                                    boost::any pointer_to_container,
                                    streamed_mutation::forwarding fwd)
{
    return make_partition_snapshot_flat_reader<partition_snapshot_reader_dummy_accounter>(std::move(s),
            std::move(dk), std::move(crr), std::move(snp), region, read_section, std::move(pointer_to_container), fwd);
}
<|endoftext|>


<commit_before>#include "arch/linux/coroutines.hpp"
#include "arch/linux/thread_pool.hpp"
#include "config/args.hpp"
#include <stdio.h>
#include <sys/mman.h>
#include <ucontext.h>

#ifdef VALGRIND
#include <valgrind/valgrind.h>
#endif

#ifndef NDEBUG
#include <cxxabi.h>   // For __cxa_current_exception_type (see below)
#endif

perfmon_counter_t pm_active_coroutines("active_coroutines"),
                  pm_allocated_coroutines("allocated_coroutines");

size_t coro_stack_size = COROUTINE_STACK_SIZE; //Default, setable by command-line parameter

/* We have a custom implementation of swapcontext() that doesn't swap the floating-point
registers, the SSE registers, or the signal mask. This is for performance reasons. */

extern "C" {
    extern int lightweight_swapcontext(ucontext_t *oucp, const ucontext_t *ucp);
}

/* The register definitions and the definition of lightweight_swapcontext itself are
derived from GLibC, which is covered under the LGPL. */

/* These are the byte offsets of various fields in the GLibC ucontext_t type. To be
more portable, maybe we should define our own makecontext()/getcontext()/setcontext()
as well; right now we're tied tightly to GLibC. */
#define oRBP "120"
#define oRSP "160"
#define oRBX "128"
#define oR12 "72"
#define oR13 "80"
#define oR14 "88"
#define oR15 "96"
#define oRIP "168"

asm(
    ".section .text\n"
    ".globl lightweight_swapcontext\n"
    "lightweight_swapcontext:\n"

    /* Save the preserved registers and the return address */
    "\tmovq\t%rbx, "oRBX"(%rdi)\n"
    "\tmovq\t%rbp, "oRBP"(%rdi)\n"
    "\tmovq\t%r12, "oR12"(%rdi)\n"
    "\tmovq\t%r13, "oR13"(%rdi)\n"
    "\tmovq\t%r14, "oR14"(%rdi)\n"
    "\tmovq\t%r15, "oR15"(%rdi)\n"
    "\tmovq\t(%rsp), %rcx\n"
    "\tmovq\t%rcx, "oRIP"(%rdi)\n"
    "\tleaq\t8(%rsp), %rcx\n"        /* Exclude the return address.  */
    "\tmovq\t%rcx, "oRSP"(%rdi)\n"

    /* Load the new stack pointer and the preserved registers.  */
    "\tmovq\t"oRSP"(%rsi), %rsp\n"
    "\tmovq\t"oRBX"(%rsi), %rbx\n"
    "\tmovq\t"oRBP"(%rsi), %rbp\n"
    "\tmovq\t"oR12"(%rsi), %r12\n"
    "\tmovq\t"oR13"(%rsi), %r13\n"
    "\tmovq\t"oR14"(%rsi), %r14\n"
    "\tmovq\t"oR15"(%rsi), %r15\n"

    /* The following ret should return to the address set with
    getcontext.  Therefore push the address on the stack.  */
    "\tmovq\t"oRIP"(%rsi), %rcx\n"
    "\tpushq\t%rcx\n"
    "\txorl\t%eax, %eax\n"
    "\tret\n"
);

/* coro_context_t is only used internally within the coroutine logic. For performance reasons,
we recycle stacks and ucontexts; the coro_context_t represents a stack and a ucontext. */

struct coro_context_t :
    public intrusive_list_node_t<coro_context_t>
{
    coro_context_t();

    /* The run() function is at the bottom of every coro_context_t's call stack. It repeatedly
    waits for a coroutine to run and then calls that coroutine's run() method. */
    static void run();

    ~coro_context_t();

    /* The guts of the coro_context_t */
    void *stack;
    ucontext_t env;
#ifdef VALGRIND
    unsigned int valgrind_stack_id;
#endif
};

/* The coroutine we're currently in, if any. NULL if we are in the main context. */
static __thread coro_t *current_coro = NULL;

/* The main context. */
static __thread ucontext_t scheduler;

/* A list of coro_context_t objects that are not in use. */
static __thread intrusive_list_t<coro_context_t> *free_contexts = NULL;

/* coro_globals_t */

coro_globals_t::coro_globals_t()
{
    rassert(!current_coro);

    rassert(free_contexts == NULL);
    free_contexts = new intrusive_list_t<coro_context_t>;
}

coro_globals_t::~coro_globals_t()
{
    rassert(!current_coro);

    /* Destroy remaining coroutines */
    while (coro_context_t *s = free_contexts->head()) {
        free_contexts->remove(s);
        delete s;
    }

    delete free_contexts;
    free_contexts = NULL;
}

/* coro_context_t */

coro_context_t::coro_context_t()
{
    pm_allocated_coroutines++;

    stack = malloc_aligned(coro_stack_size, getpagesize());

    /* Protect the end of the stack so that we crash when we get a stack overflow instead of
    corrupting memory. */
    mprotect(stack, getpagesize(), PROT_NONE);

    /* Register our stack with Valgrind so that it understands what's going on and doesn't
    create spurious errors */
#ifdef VALGRIND
    valgrind_stack_id = VALGRIND_STACK_REGISTER(stack, (intptr_t)stack + coro_stack_size);
#endif

    getcontext(&env);   /* Why do we call getcontext() here? */

    env.uc_stack.ss_sp = stack;
    env.uc_stack.ss_size = coro_stack_size;
    env.uc_stack.ss_flags = 0;
    env.uc_link = NULL;

    /* run() is the main worker loop for a coroutine. */
    makecontext(&env, &coro_context_t::run, 0);
}

void coro_context_t::run() {

    coro_context_t *self = current_coro->context;

    /* Make sure we're on the right stack. */
#ifndef NDEBUG
    char dummy;
    rassert(&dummy >= (char*)self->stack);
    rassert(&dummy < (char*)self->stack + coro_stack_size);
#endif

    while (true) {

        current_coro->run();

        current_coro = NULL;
        lightweight_swapcontext(&self->env, &scheduler);
    }
}

coro_context_t::~coro_context_t() {

#ifdef VALGRIND
    VALGRIND_STACK_DEREGISTER(valgrind_stack_id);
#endif

    mprotect(stack, getpagesize(), PROT_READ|PROT_WRITE); //Undo protections changes

    free(stack);

    pm_allocated_coroutines--;
}

/* coro_t */

coro_t::coro_t(deed_t *deed) :
    deed(deed),
    current_thread(linux_thread_pool_t::thread_id),
    notified(false)
{

    pm_active_coroutines++;

    /* Find us a stack */
    if (free_contexts->size() == 0) {
        context = new coro_context_t();
    } else {
        context = free_contexts->tail();
        free_contexts->remove(context);
    }

    /* Eventually our run() method will be called within the coroutine. */
    notify();
}

coro_t::~coro_t() {

    /* Return the context to the free-contexts list */
    free_contexts->push_back(context);

    pm_active_coroutines--;
}

void coro_t::run() {

    rassert(current_coro == this);

    (*deed)();

    delete this;
}

coro_t *coro_t::self() {   /* class method */
    return current_coro;
}

void coro_t::wait() {   /* class method */

#ifndef NDEBUG
    /* It's not safe to wait() in a catch clause of an exception handler. We use the non-standard
    GCC-only interface "cxxabi.h" to figure out if we're in the catch clause of an exception
    handler. In C++0x we will be able to use std::current_exception() instead. */
    rassert(!abi::__cxa_current_exception_type());
#endif

    rassert(current_coro != NULL);
    coro_context_t *context = current_coro->context;
    current_coro = NULL;
    lightweight_swapcontext(&context->env, &scheduler);
    rassert(current_coro != NULL);
}

void coro_t::notify() {

    rassert(!notified);
    notified = true;

    /* notify() doesn't switch to the coroutine immediately; instead, it just pushes
    the coroutine onto the event queue. */

    /* current_thread is the thread that the coroutine lives on, which may or may not be the
    same as get_thread_id(). */
    linux_thread_pool_t::thread->message_hub.store_message(current_thread, this);
}

void coro_t::move_to_thread(int thread) {   /* class method */
    self()->current_thread = thread;
    self()->notify();
    wait();
}

void coro_t::on_thread_switch() {

    rassert(notified);
    notified = false;

    rassert(current_coro == NULL);
    current_coro = this;
    lightweight_swapcontext(&scheduler, &context->env);
    rassert(current_coro == NULL);
}

void coro_t::set_coroutine_stack_size(size_t size) {
    coro_stack_size = size;
}

/* Called by SIGSEGV handler to identify segfaults that come from overflowing a coroutine's
stack. Could also in theory be used by a function to check if it's about to overflow
the stack. */

bool is_coroutine_stack_overflow(void *addr) {
    void *base = (void *)floor_aligned((intptr_t)addr, getpagesize());
    return current_coro && current_coro->context->stack == base;
}
<commit_msg>Do nothing when move_to_thread()ing to the current thread.<commit_after>#include "arch/linux/coroutines.hpp"
#include "arch/linux/thread_pool.hpp"
#include "config/args.hpp"
#include <stdio.h>
#include <sys/mman.h>
#include <ucontext.h>

#ifdef VALGRIND
#include <valgrind/valgrind.h>
#endif

#ifndef NDEBUG
#include <cxxabi.h>   // For __cxa_current_exception_type (see below)
#endif

perfmon_counter_t pm_active_coroutines("active_coroutines"),
                  pm_allocated_coroutines("allocated_coroutines");

size_t coro_stack_size = COROUTINE_STACK_SIZE; //Default, setable by command-line parameter

/* We have a custom implementation of swapcontext() that doesn't swap the floating-point
registers, the SSE registers, or the signal mask. This is for performance reasons. */

extern "C" {
    extern int lightweight_swapcontext(ucontext_t *oucp, const ucontext_t *ucp);
}

/* The register definitions and the definition of lightweight_swapcontext itself are
derived from GLibC, which is covered under the LGPL. */

/* These are the byte offsets of various fields in the GLibC ucontext_t type. To be
more portable, maybe we should define our own makecontext()/getcontext()/setcontext()
as well; right now we're tied tightly to GLibC. */
#define oRBP "120"
#define oRSP "160"
#define oRBX "128"
#define oR12 "72"
#define oR13 "80"
#define oR14 "88"
#define oR15 "96"
#define oRIP "168"

asm(
    ".section .text\n"
    ".globl lightweight_swapcontext\n"
    "lightweight_swapcontext:\n"

    /* Save the preserved registers and the return address */
    "\tmovq\t%rbx, "oRBX"(%rdi)\n"
    "\tmovq\t%rbp, "oRBP"(%rdi)\n"
    "\tmovq\t%r12, "oR12"(%rdi)\n"
    "\tmovq\t%r13, "oR13"(%rdi)\n"
    "\tmovq\t%r14, "oR14"(%rdi)\n"
    "\tmovq\t%r15, "oR15"(%rdi)\n"
    "\tmovq\t(%rsp), %rcx\n"
    "\tmovq\t%rcx, "oRIP"(%rdi)\n"
    "\tleaq\t8(%rsp), %rcx\n"        /* Exclude the return address.  */
    "\tmovq\t%rcx, "oRSP"(%rdi)\n"

    /* Load the new stack pointer and the preserved registers.  */
    "\tmovq\t"oRSP"(%rsi), %rsp\n"
    "\tmovq\t"oRBX"(%rsi), %rbx\n"
    "\tmovq\t"oRBP"(%rsi), %rbp\n"
    "\tmovq\t"oR12"(%rsi), %r12\n"
    "\tmovq\t"oR13"(%rsi), %r13\n"
    "\tmovq\t"oR14"(%rsi), %r14\n"
    "\tmovq\t"oR15"(%rsi), %r15\n"

    /* The following ret should return to the address set with
    getcontext.  Therefore push the address on the stack.  */
    "\tmovq\t"oRIP"(%rsi), %rcx\n"
    "\tpushq\t%rcx\n"
    "\txorl\t%eax, %eax\n"
    "\tret\n"
);

/* coro_context_t is only used internally within the coroutine logic. For performance reasons,
we recycle stacks and ucontexts; the coro_context_t represents a stack and a ucontext. */

struct coro_context_t :
    public intrusive_list_node_t<coro_context_t>
{
    coro_context_t();

    /* The run() function is at the bottom of every coro_context_t's call stack. It repeatedly
    waits for a coroutine to run and then calls that coroutine's run() method. */
    static void run();

    ~coro_context_t();

    /* The guts of the coro_context_t */
    void *stack;
    ucontext_t env;
#ifdef VALGRIND
    unsigned int valgrind_stack_id;
#endif
};

/* The coroutine we're currently in, if any. NULL if we are in the main context. */
static __thread coro_t *current_coro = NULL;

/* The main context. */
static __thread ucontext_t scheduler;

/* A list of coro_context_t objects that are not in use. */
static __thread intrusive_list_t<coro_context_t> *free_contexts = NULL;

/* coro_globals_t */

coro_globals_t::coro_globals_t()
{
    rassert(!current_coro);

    rassert(free_contexts == NULL);
    free_contexts = new intrusive_list_t<coro_context_t>;
}

coro_globals_t::~coro_globals_t()
{
    rassert(!current_coro);

    /* Destroy remaining coroutines */
    while (coro_context_t *s = free_contexts->head()) {
        free_contexts->remove(s);
        delete s;
    }

    delete free_contexts;
    free_contexts = NULL;
}

/* coro_context_t */

coro_context_t::coro_context_t()
{
    pm_allocated_coroutines++;

    stack = malloc_aligned(coro_stack_size, getpagesize());

    /* Protect the end of the stack so that we crash when we get a stack overflow instead of
    corrupting memory. */
    mprotect(stack, getpagesize(), PROT_NONE);

    /* Register our stack with Valgrind so that it understands what's going on and doesn't
    create spurious errors */
#ifdef VALGRIND
    valgrind_stack_id = VALGRIND_STACK_REGISTER(stack, (intptr_t)stack + coro_stack_size);
#endif

    getcontext(&env);   /* Why do we call getcontext() here? */

    env.uc_stack.ss_sp = stack;
    env.uc_stack.ss_size = coro_stack_size;
    env.uc_stack.ss_flags = 0;
    env.uc_link = NULL;

    /* run() is the main worker loop for a coroutine. */
    makecontext(&env, &coro_context_t::run, 0);
}

void coro_context_t::run() {

    coro_context_t *self = current_coro->context;

    /* Make sure we're on the right stack. */
#ifndef NDEBUG
    char dummy;
    rassert(&dummy >= (char*)self->stack);
    rassert(&dummy < (char*)self->stack + coro_stack_size);
#endif

    while (true) {

        current_coro->run();

        current_coro = NULL;
        lightweight_swapcontext(&self->env, &scheduler);
    }
}

coro_context_t::~coro_context_t() {

#ifdef VALGRIND
    VALGRIND_STACK_DEREGISTER(valgrind_stack_id);
#endif

    mprotect(stack, getpagesize(), PROT_READ|PROT_WRITE); //Undo protections changes

    free(stack);

    pm_allocated_coroutines--;
}

/* coro_t */

coro_t::coro_t(deed_t *deed) :
    deed(deed),
    current_thread(linux_thread_pool_t::thread_id),
    notified(false)
{

    pm_active_coroutines++;

    /* Find us a stack */
    if (free_contexts->size() == 0) {
        context = new coro_context_t();
    } else {
        context = free_contexts->tail();
        free_contexts->remove(context);
    }

    /* Eventually our run() method will be called within the coroutine. */
    notify();
}

coro_t::~coro_t() {

    /* Return the context to the free-contexts list */
    free_contexts->push_back(context);

    pm_active_coroutines--;
}

void coro_t::run() {

    rassert(current_coro == this);

    (*deed)();

    delete this;
}

coro_t *coro_t::self() {   /* class method */
    return current_coro;
}

void coro_t::wait() {   /* class method */

#ifndef NDEBUG
    /* It's not safe to wait() in a catch clause of an exception handler. We use the non-standard
    GCC-only interface "cxxabi.h" to figure out if we're in the catch clause of an exception
    handler. In C++0x we will be able to use std::current_exception() instead. */
    rassert(!abi::__cxa_current_exception_type());
#endif

    rassert(current_coro != NULL);
    coro_context_t *context = current_coro->context;
    current_coro = NULL;
    lightweight_swapcontext(&context->env, &scheduler);
    rassert(current_coro != NULL);
}

void coro_t::notify() {

    rassert(!notified);
    notified = true;

    /* notify() doesn't switch to the coroutine immediately; instead, it just pushes
    the coroutine onto the event queue. */

    /* current_thread is the thread that the coroutine lives on, which may or may not be the
    same as get_thread_id(). */
    linux_thread_pool_t::thread->message_hub.store_message(current_thread, this);
}

void coro_t::move_to_thread(int thread) {   /* class method */
    if (thread == self()->current_thread) {
        // If we're trying to switch to the thread we're currently on, do nothing.
        return;
    }
    self()->current_thread = thread;
    self()->notify();
    wait();
}

void coro_t::on_thread_switch() {

    rassert(notified);
    notified = false;

    rassert(current_coro == NULL);
    current_coro = this;
    lightweight_swapcontext(&scheduler, &context->env);
    rassert(current_coro == NULL);
}

void coro_t::set_coroutine_stack_size(size_t size) {
    coro_stack_size = size;
}

/* Called by SIGSEGV handler to identify segfaults that come from overflowing a coroutine's
stack. Could also in theory be used by a function to check if it's about to overflow
the stack. */

bool is_coroutine_stack_overflow(void *addr) {
    void *base = (void *)floor_aligned((intptr_t)addr, getpagesize());
    return current_coro && current_coro->context->stack == base;
}
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2003-2005 The Regents of The University of Michigan
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met: redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer;
 * redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution;
 * neither the name of the copyright holders nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * Authors: Gabe Black
 */

#ifndef __ARCH_SPARC_ISA_TRAITS_HH__
#define __ARCH_SPARC_ISA_TRAITS_HH__

#include "arch/sparc/types.hh"
#include "base/misc.hh"
#include "config/full_system.hh"
#include "sim/host.hh"

class ThreadContext;
class FastCPU;
//class FullCPU;
class Checkpoint;

class StaticInst;
class StaticInstPtr;

namespace BigEndianGuest {}

#if FULL_SYSTEM
#include "arch/sparc/isa_fullsys_traits.hh"
#endif

namespace SparcISA
{
    class RegFile;

    //This makes sure the big endian versions of certain functions are used.
    using namespace BigEndianGuest;

    // SPARC has a delay slot
    #define ISA_HAS_DELAY_SLOT 1

    // SPARC NOP (sethi %(hi(0), g0)
    const MachInst NoopMachInst = 0x01000000;

    const int NumRegularIntRegs = 32;
    const int NumMicroIntRegs = 1;
    const int NumIntRegs =
        NumRegularIntRegs +
        NumMicroIntRegs;
    const int NumFloatRegs = 64;
    const int NumMiscRegs = 40;

    // These enumerate all the registers for dependence tracking.
    enum DependenceTags {
        // 0..31 are the integer regs 0..31
        // 32..95 are the FP regs 0..31, i.e. use (reg + FP_Base_DepTag)
        FP_Base_DepTag = NumIntRegs,
        Ctrl_Base_DepTag = NumIntRegs + NumFloatRegs,
        //XXX These are here solely to get compilation and won't work
        Fpcr_DepTag = 0,
        Uniq_DepTag = 0
    };

    // MAXTL - maximum trap level
    const int MaxPTL = 2;
    const int MaxTL  = 6;
    const int MaxGL  = 3;
    const int MaxPGL = 2;

    // NWINDOWS - number of register windows, can be 3 to 32
    const int NWindows = 8;

    // semantically meaningful register indices
    const int ZeroReg = 0;	// architecturally meaningful
    // the rest of these depend on the ABI
    const int StackPointerReg = 14;
    const int ReturnAddressReg = 31; // post call, precall is 15
    const int ReturnValueReg = 8; // Post return, 24 is pre-return.
    const int FramePointerReg = 30;
    const int ArgumentReg0 = 8;
    const int ArgumentReg1 = 9;
    const int ArgumentReg2 = 10;
    const int ArgumentReg3 = 11;
    const int ArgumentReg4 = 12;
    const int ArgumentReg5 = 13;
    // Some OS syscall use a second register (o1) to return a second value
    const int SyscallPseudoReturnReg = ArgumentReg1;

    //XXX These numbers are bogus
    const int MaxInstSrcRegs = 8;
    const int MaxInstDestRegs = 9;

    //8K. This value is implmentation specific; and should probably
    //be somewhere else.
    const int LogVMPageSize = 13;
    const int VMPageSize = (1 << LogVMPageSize);

    //Why does both the previous set of constants and this one exist?
    const int PageShift = 13;
    const int PageBytes = ULL(1) << PageShift;

    const int BranchPredAddrShiftAmt = 2;

    const int MachineBytes = 8;
    const int WordBytes = 4;
    const int HalfwordBytes = 2;
    const int ByteBytes = 1;

    void serialize(std::ostream & os);

    void unserialize(Checkpoint *cp, const std::string &section);

    StaticInstPtr decodeInst(ExtMachInst);

    // return a no-op instruction... used for instruction fetch faults
    extern const MachInst NoopMachInst;
}

#endif // __ARCH_SPARC_ISA_TRAITS_HH__
<commit_msg>Fixed up DepTags a little. I think NumMicroIntRegs shouldn't be added to Ctrl_Base_DepTag.<commit_after>/*
 * Copyright (c) 2003-2005 The Regents of The University of Michigan
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met: redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer;
 * redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution;
 * neither the name of the copyright holders nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * Authors: Gabe Black
 */

#ifndef __ARCH_SPARC_ISA_TRAITS_HH__
#define __ARCH_SPARC_ISA_TRAITS_HH__

#include "arch/sparc/types.hh"
#include "base/misc.hh"
#include "config/full_system.hh"
#include "sim/host.hh"

class ThreadContext;
class FastCPU;
//class FullCPU;
class Checkpoint;

class StaticInst;
class StaticInstPtr;

namespace BigEndianGuest {}

#if FULL_SYSTEM
#include "arch/sparc/isa_fullsys_traits.hh"
#endif

namespace SparcISA
{
    class RegFile;

    //This makes sure the big endian versions of certain functions are used.
    using namespace BigEndianGuest;

    // SPARC has a delay slot
    #define ISA_HAS_DELAY_SLOT 1

    // SPARC NOP (sethi %(hi(0), g0)
    const MachInst NoopMachInst = 0x01000000;

    const int NumRegularIntRegs = 32;
    const int NumMicroIntRegs = 1;
    const int NumIntRegs =
        NumRegularIntRegs +
        NumMicroIntRegs;
    const int NumFloatRegs = 64;
    const int NumMiscRegs = 40;

    // These enumerate all the registers for dependence tracking.
    enum DependenceTags {
        // 0..31 are the integer regs 0..31
        // 32..95 are the FP regs 0..31, i.e. use (reg + FP_Base_DepTag)
        FP_Base_DepTag = NumIntRegs,
        Ctrl_Base_DepTag = NumIntRegs + NumMicroIntRegs + NumFloatRegs,
    };

    // MAXTL - maximum trap level
    const int MaxPTL = 2;
    const int MaxTL  = 6;
    const int MaxGL  = 3;
    const int MaxPGL = 2;

    // NWINDOWS - number of register windows, can be 3 to 32
    const int NWindows = 8;

    // semantically meaningful register indices
    const int ZeroReg = 0;	// architecturally meaningful
    // the rest of these depend on the ABI
    const int StackPointerReg = 14;
    const int ReturnAddressReg = 31; // post call, precall is 15
    const int ReturnValueReg = 8; // Post return, 24 is pre-return.
    const int FramePointerReg = 30;
    const int ArgumentReg0 = 8;
    const int ArgumentReg1 = 9;
    const int ArgumentReg2 = 10;
    const int ArgumentReg3 = 11;
    const int ArgumentReg4 = 12;
    const int ArgumentReg5 = 13;
    // Some OS syscall use a second register (o1) to return a second value
    const int SyscallPseudoReturnReg = ArgumentReg1;

    //XXX These numbers are bogus
    const int MaxInstSrcRegs = 8;
    const int MaxInstDestRegs = 9;

    //8K. This value is implmentation specific; and should probably
    //be somewhere else.
    const int LogVMPageSize = 13;
    const int VMPageSize = (1 << LogVMPageSize);

    //Why does both the previous set of constants and this one exist?
    const int PageShift = 13;
    const int PageBytes = ULL(1) << PageShift;

    const int BranchPredAddrShiftAmt = 2;

    const int MachineBytes = 8;
    const int WordBytes = 4;
    const int HalfwordBytes = 2;
    const int ByteBytes = 1;

    void serialize(std::ostream & os);

    void unserialize(Checkpoint *cp, const std::string &section);

    StaticInstPtr decodeInst(ExtMachInst);

    // return a no-op instruction... used for instruction fetch faults
    extern const MachInst NoopMachInst;
}

#endif // __ARCH_SPARC_ISA_TRAITS_HH__
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2015 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 <boost/range/iterator_range.hpp>

#include "bytes.hh"
#include "core/unaligned.hh"
#include "hashing.hh"
#include "seastar/core/simple-stream.hh"
/**
 * Utility for writing data into a buffer when its final size is not known up front.
 *
 * Internally the data is written into a chain of chunks allocated on-demand.
 * No resizing of previously written data happens.
 *
 */
class bytes_ostream {
public:
    using size_type = bytes::size_type;
    using value_type = bytes::value_type;
    static constexpr size_type max_chunk_size() { return 16 * 1024; }
private:
    static_assert(sizeof(value_type) == 1, "value_type is assumed to be one byte long");
    struct chunk {
        // FIXME: group fragment pointers to reduce pointer chasing when packetizing
        std::unique_ptr<chunk> next;
        ~chunk() {
            auto p = std::move(next);
            while (p) {
                // Avoid recursion when freeing chunks
                auto p_next = std::move(p->next);
                p = std::move(p_next);
            }
        }
        size_type offset; // Also means "size" after chunk is closed
        size_type size;
        value_type data[0];
        void operator delete(void* ptr) { free(ptr); }
    };
    // FIXME: consider increasing chunk size as the buffer grows
    static constexpr size_type chunk_size{512};
private:
    std::unique_ptr<chunk> _begin;
    chunk* _current;
    size_type _size;
public:
    class fragment_iterator : public std::iterator<std::input_iterator_tag, bytes_view> {
        chunk* _current;
    public:
        fragment_iterator(chunk* current) : _current(current) {}
        fragment_iterator(const fragment_iterator&) = default;
        fragment_iterator& operator=(const fragment_iterator&) = default;
        bytes_view operator*() const {
            return { _current->data, _current->offset };
        }
        bytes_view operator->() const {
            return *(*this);
        }
        fragment_iterator& operator++() {
            _current = _current->next.get();
            return *this;
        }
        fragment_iterator operator++(int) {
            fragment_iterator tmp(*this);
            ++(*this);
            return tmp;
        }
        bool operator==(const fragment_iterator& other) const {
            return _current == other._current;
        }
        bool operator!=(const fragment_iterator& other) const {
            return _current != other._current;
        }
    };
private:
    inline size_type current_space_left() const {
        if (!_current) {
            return 0;
        }
        return _current->size - _current->offset;
    }
    // Figure out next chunk size.
    //   - must be enough for data_size
    //   - must be at least chunk_size
    //   - try to double each time to prevent too many allocations
    //   - do not exceed max_chunk_size
    size_type next_alloc_size(size_t data_size) const {
        auto next_size = _current
                ? _current->size * 2
                : chunk_size;
        next_size = std::min(next_size, max_chunk_size());
        // FIXME: check for overflow?
        return std::max<size_type>(next_size, data_size + sizeof(chunk));
    }
    // Makes room for a contiguous region of given size.
    // The region is accounted for as already written.
    // size must not be zero.
    value_type* alloc(size_type size) {
        if (size <= current_space_left()) {
            auto ret = _current->data + _current->offset;
            _current->offset += size;
            _size += size;
            return ret;
        } else {
            auto alloc_size = next_alloc_size(size);
            auto space = malloc(alloc_size);
            if (!space) {
                throw std::bad_alloc();
            }
            auto new_chunk = std::unique_ptr<chunk>(new (space) chunk());
            new_chunk->offset = size;
            new_chunk->size = alloc_size - sizeof(chunk);
            if (_current) {
                _current->next = std::move(new_chunk);
                _current = _current->next.get();
            } else {
                _begin = std::move(new_chunk);
                _current = _begin.get();
            }
            _size += size;
            return _current->data;
        };
    }
public:
    bytes_ostream() noexcept
        : _begin()
        , _current(nullptr)
        , _size(0)
    { }

    bytes_ostream(bytes_ostream&& o) noexcept
        : _begin(std::move(o._begin))
        , _current(o._current)
        , _size(o._size)
    {
        o._current = nullptr;
        o._size = 0;
    }

    bytes_ostream(const bytes_ostream& o)
        : _begin()
        , _current(nullptr)
        , _size(0)
    {
        append(o);
    }

    bytes_ostream& operator=(const bytes_ostream& o) {
        if (this != &o) {
            auto x = bytes_ostream(o);
            *this = std::move(x);
        }
        return *this;
    }

    bytes_ostream& operator=(bytes_ostream&& o) noexcept {
        if (this != &o) {
            this->~bytes_ostream();
            new (this) bytes_ostream(std::move(o));
        }
        return *this;
    }

    template <typename T>
    struct place_holder {
        value_type* ptr;
        // makes the place_holder looks like a stream
        seastar::simple_output_stream get_stream() {
            return seastar::simple_output_stream(reinterpret_cast<char*>(ptr), sizeof(T));
        }
    };

    // Returns a place holder for a value to be written later.
    template <typename T>
    inline
    std::enable_if_t<std::is_fundamental<T>::value, place_holder<T>>
    write_place_holder() {
        return place_holder<T>{alloc(sizeof(T))};
    }

    value_type* write_place_holder(size_type size) {
        return alloc(size);
    }

    // Writes given sequence of bytes
    inline void write(bytes_view v) {
        if (v.empty()) {
            return;
        }

        auto this_size = std::min(v.size(), size_t(current_space_left()));
        if (this_size) {
            memcpy(_current->data + _current->offset, v.begin(), this_size);
            _current->offset += this_size;
            _size += this_size;
            v.remove_prefix(this_size);
        }

        while (!v.empty()) {
            auto this_size = std::min(v.size(), size_t(max_chunk_size()));
            std::copy_n(v.begin(), this_size, alloc(this_size));
            v.remove_prefix(this_size);
        }
    }

    void write(const char* ptr, size_t size) {
        write(bytes_view(reinterpret_cast<const signed char*>(ptr), size));
    }

    bool is_linearized() const {
        return !_begin || !_begin->next;
    }

    // Call only when is_linearized()
    bytes_view view() const {
        assert(is_linearized());
        if (!_current) {
            return bytes_view();
        }

        return bytes_view(_current->data, _size);
    }

    // Makes the underlying storage contiguous and returns a view to it.
    // Invalidates all previously created placeholders.
    bytes_view linearize() {
        if (is_linearized()) {
            return view();
        }

        auto space = malloc(_size + sizeof(chunk));
        if (!space) {
            throw std::bad_alloc();
        }

        auto new_chunk = std::unique_ptr<chunk>(new (space) chunk());
        new_chunk->offset = _size;
        new_chunk->size = _size;

        auto dst = new_chunk->data;
        auto r = _begin.get();
        while (r) {
            auto next = r->next.get();
            dst = std::copy_n(r->data, r->offset, dst);
            r = next;
        }

        _current = new_chunk.get();
        _begin = std::move(new_chunk);
        return bytes_view(_current->data, _size);
    }

    // Returns the amount of bytes written so far
    size_type size() const {
        return _size;
    }

    bool empty() const {
        return _size == 0;
    }

    void reserve(size_t size) {
        // FIXME: implement
    }

    void append(const bytes_ostream& o) {
        for (auto&& bv : o.fragments()) {
            write(bv);
        }
    }

    // begin() and end() form an input range to bytes_view representing fragments.
    // Any modification of this instance invalidates iterators.
    fragment_iterator begin() const { return { _begin.get() }; }
    fragment_iterator end() const { return { nullptr }; }

    boost::iterator_range<fragment_iterator> fragments() const {
        return { begin(), end() };
    }

    struct position {
        chunk* _chunk;
        size_type _offset;
    };

    position pos() const {
        return { _current, _current ? _current->offset : 0 };
    }

    // Returns the amount of bytes written since given position.
    // "pos" must be valid.
    size_type written_since(position pos) {
        chunk* c = pos._chunk;
        if (!c) {
            return _size;
        }
        size_type total = c->offset - pos._offset;
        c = c->next.get();
        while (c) {
            total += c->offset;
            c = c->next.get();
        }
        return total;
    }

    // Rollbacks all data written after "pos".
    // Invalidates all placeholders and positions created after "pos".
    void retract(position pos) {
        if (!pos._chunk) {
            *this = {};
            return;
        }
        _size -= written_since(pos);
        _current = pos._chunk;
        _current->next = nullptr;
        _current->offset = pos._offset;
    }

    void reduce_chunk_count() {
        // FIXME: This is a simplified version. It linearizes the whole buffer
        // if its size is below max_chunk_size. We probably could also gain
        // some read performance by doing "real" reduction, i.e. merging
        // all chunks until all but the last one is max_chunk_size.
        if (size() < max_chunk_size()) {
            linearize();
        }
    }

    bool operator==(const bytes_ostream& other) const {
        auto as = fragments().begin();
        auto as_end = fragments().end();
        auto bs = other.fragments().begin();
        auto bs_end = other.fragments().end();

        auto a = *as++;
        auto b = *bs++;
        while (!a.empty() || !b.empty()) {
            auto now = std::min(a.size(), b.size());
            if (!std::equal(a.begin(), a.begin() + now, b.begin(), b.begin() + now)) {
                return false;
            }
            a.remove_prefix(now);
            if (a.empty() && as != as_end) {
                a = *as++;
            }
            b.remove_prefix(now);
            if (b.empty() && bs != bs_end) {
                b = *bs++;
            }
        }
        return true;
    }

    bool operator!=(const bytes_ostream& other) const {
        return !(*this == other);
    }
};

template<>
struct appending_hash<bytes_ostream> {
    template<typename Hasher>
    void operator()(Hasher& h, const bytes_ostream& b) const {
        for (auto&& frag : b.fragments()) {
            feed_hash(h, frag);
        }
    }
};
<commit_msg>bytes_ostream: make fragment_iterator default constructible<commit_after>/*
 * Copyright (C) 2015 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 <boost/range/iterator_range.hpp>

#include "bytes.hh"
#include "core/unaligned.hh"
#include "hashing.hh"
#include "seastar/core/simple-stream.hh"
/**
 * Utility for writing data into a buffer when its final size is not known up front.
 *
 * Internally the data is written into a chain of chunks allocated on-demand.
 * No resizing of previously written data happens.
 *
 */
class bytes_ostream {
public:
    using size_type = bytes::size_type;
    using value_type = bytes::value_type;
    static constexpr size_type max_chunk_size() { return 16 * 1024; }
private:
    static_assert(sizeof(value_type) == 1, "value_type is assumed to be one byte long");
    struct chunk {
        // FIXME: group fragment pointers to reduce pointer chasing when packetizing
        std::unique_ptr<chunk> next;
        ~chunk() {
            auto p = std::move(next);
            while (p) {
                // Avoid recursion when freeing chunks
                auto p_next = std::move(p->next);
                p = std::move(p_next);
            }
        }
        size_type offset; // Also means "size" after chunk is closed
        size_type size;
        value_type data[0];
        void operator delete(void* ptr) { free(ptr); }
    };
    // FIXME: consider increasing chunk size as the buffer grows
    static constexpr size_type chunk_size{512};
private:
    std::unique_ptr<chunk> _begin;
    chunk* _current;
    size_type _size;
public:
    class fragment_iterator : public std::iterator<std::input_iterator_tag, bytes_view> {
        chunk* _current = nullptr;
    public:
        fragment_iterator() = default;
        fragment_iterator(chunk* current) : _current(current) {}
        fragment_iterator(const fragment_iterator&) = default;
        fragment_iterator& operator=(const fragment_iterator&) = default;
        bytes_view operator*() const {
            return { _current->data, _current->offset };
        }
        bytes_view operator->() const {
            return *(*this);
        }
        fragment_iterator& operator++() {
            _current = _current->next.get();
            return *this;
        }
        fragment_iterator operator++(int) {
            fragment_iterator tmp(*this);
            ++(*this);
            return tmp;
        }
        bool operator==(const fragment_iterator& other) const {
            return _current == other._current;
        }
        bool operator!=(const fragment_iterator& other) const {
            return _current != other._current;
        }
    };
private:
    inline size_type current_space_left() const {
        if (!_current) {
            return 0;
        }
        return _current->size - _current->offset;
    }
    // Figure out next chunk size.
    //   - must be enough for data_size
    //   - must be at least chunk_size
    //   - try to double each time to prevent too many allocations
    //   - do not exceed max_chunk_size
    size_type next_alloc_size(size_t data_size) const {
        auto next_size = _current
                ? _current->size * 2
                : chunk_size;
        next_size = std::min(next_size, max_chunk_size());
        // FIXME: check for overflow?
        return std::max<size_type>(next_size, data_size + sizeof(chunk));
    }
    // Makes room for a contiguous region of given size.
    // The region is accounted for as already written.
    // size must not be zero.
    value_type* alloc(size_type size) {
        if (size <= current_space_left()) {
            auto ret = _current->data + _current->offset;
            _current->offset += size;
            _size += size;
            return ret;
        } else {
            auto alloc_size = next_alloc_size(size);
            auto space = malloc(alloc_size);
            if (!space) {
                throw std::bad_alloc();
            }
            auto new_chunk = std::unique_ptr<chunk>(new (space) chunk());
            new_chunk->offset = size;
            new_chunk->size = alloc_size - sizeof(chunk);
            if (_current) {
                _current->next = std::move(new_chunk);
                _current = _current->next.get();
            } else {
                _begin = std::move(new_chunk);
                _current = _begin.get();
            }
            _size += size;
            return _current->data;
        };
    }
public:
    bytes_ostream() noexcept
        : _begin()
        , _current(nullptr)
        , _size(0)
    { }

    bytes_ostream(bytes_ostream&& o) noexcept
        : _begin(std::move(o._begin))
        , _current(o._current)
        , _size(o._size)
    {
        o._current = nullptr;
        o._size = 0;
    }

    bytes_ostream(const bytes_ostream& o)
        : _begin()
        , _current(nullptr)
        , _size(0)
    {
        append(o);
    }

    bytes_ostream& operator=(const bytes_ostream& o) {
        if (this != &o) {
            auto x = bytes_ostream(o);
            *this = std::move(x);
        }
        return *this;
    }

    bytes_ostream& operator=(bytes_ostream&& o) noexcept {
        if (this != &o) {
            this->~bytes_ostream();
            new (this) bytes_ostream(std::move(o));
        }
        return *this;
    }

    template <typename T>
    struct place_holder {
        value_type* ptr;
        // makes the place_holder looks like a stream
        seastar::simple_output_stream get_stream() {
            return seastar::simple_output_stream(reinterpret_cast<char*>(ptr), sizeof(T));
        }
    };

    // Returns a place holder for a value to be written later.
    template <typename T>
    inline
    std::enable_if_t<std::is_fundamental<T>::value, place_holder<T>>
    write_place_holder() {
        return place_holder<T>{alloc(sizeof(T))};
    }

    value_type* write_place_holder(size_type size) {
        return alloc(size);
    }

    // Writes given sequence of bytes
    inline void write(bytes_view v) {
        if (v.empty()) {
            return;
        }

        auto this_size = std::min(v.size(), size_t(current_space_left()));
        if (this_size) {
            memcpy(_current->data + _current->offset, v.begin(), this_size);
            _current->offset += this_size;
            _size += this_size;
            v.remove_prefix(this_size);
        }

        while (!v.empty()) {
            auto this_size = std::min(v.size(), size_t(max_chunk_size()));
            std::copy_n(v.begin(), this_size, alloc(this_size));
            v.remove_prefix(this_size);
        }
    }

    void write(const char* ptr, size_t size) {
        write(bytes_view(reinterpret_cast<const signed char*>(ptr), size));
    }

    bool is_linearized() const {
        return !_begin || !_begin->next;
    }

    // Call only when is_linearized()
    bytes_view view() const {
        assert(is_linearized());
        if (!_current) {
            return bytes_view();
        }

        return bytes_view(_current->data, _size);
    }

    // Makes the underlying storage contiguous and returns a view to it.
    // Invalidates all previously created placeholders.
    bytes_view linearize() {
        if (is_linearized()) {
            return view();
        }

        auto space = malloc(_size + sizeof(chunk));
        if (!space) {
            throw std::bad_alloc();
        }

        auto new_chunk = std::unique_ptr<chunk>(new (space) chunk());
        new_chunk->offset = _size;
        new_chunk->size = _size;

        auto dst = new_chunk->data;
        auto r = _begin.get();
        while (r) {
            auto next = r->next.get();
            dst = std::copy_n(r->data, r->offset, dst);
            r = next;
        }

        _current = new_chunk.get();
        _begin = std::move(new_chunk);
        return bytes_view(_current->data, _size);
    }

    // Returns the amount of bytes written so far
    size_type size() const {
        return _size;
    }

    bool empty() const {
        return _size == 0;
    }

    void reserve(size_t size) {
        // FIXME: implement
    }

    void append(const bytes_ostream& o) {
        for (auto&& bv : o.fragments()) {
            write(bv);
        }
    }

    // begin() and end() form an input range to bytes_view representing fragments.
    // Any modification of this instance invalidates iterators.
    fragment_iterator begin() const { return { _begin.get() }; }
    fragment_iterator end() const { return { nullptr }; }

    boost::iterator_range<fragment_iterator> fragments() const {
        return { begin(), end() };
    }

    struct position {
        chunk* _chunk;
        size_type _offset;
    };

    position pos() const {
        return { _current, _current ? _current->offset : 0 };
    }

    // Returns the amount of bytes written since given position.
    // "pos" must be valid.
    size_type written_since(position pos) {
        chunk* c = pos._chunk;
        if (!c) {
            return _size;
        }
        size_type total = c->offset - pos._offset;
        c = c->next.get();
        while (c) {
            total += c->offset;
            c = c->next.get();
        }
        return total;
    }

    // Rollbacks all data written after "pos".
    // Invalidates all placeholders and positions created after "pos".
    void retract(position pos) {
        if (!pos._chunk) {
            *this = {};
            return;
        }
        _size -= written_since(pos);
        _current = pos._chunk;
        _current->next = nullptr;
        _current->offset = pos._offset;
    }

    void reduce_chunk_count() {
        // FIXME: This is a simplified version. It linearizes the whole buffer
        // if its size is below max_chunk_size. We probably could also gain
        // some read performance by doing "real" reduction, i.e. merging
        // all chunks until all but the last one is max_chunk_size.
        if (size() < max_chunk_size()) {
            linearize();
        }
    }

    bool operator==(const bytes_ostream& other) const {
        auto as = fragments().begin();
        auto as_end = fragments().end();
        auto bs = other.fragments().begin();
        auto bs_end = other.fragments().end();

        auto a = *as++;
        auto b = *bs++;
        while (!a.empty() || !b.empty()) {
            auto now = std::min(a.size(), b.size());
            if (!std::equal(a.begin(), a.begin() + now, b.begin(), b.begin() + now)) {
                return false;
            }
            a.remove_prefix(now);
            if (a.empty() && as != as_end) {
                a = *as++;
            }
            b.remove_prefix(now);
            if (b.empty() && bs != bs_end) {
                b = *bs++;
            }
        }
        return true;
    }

    bool operator!=(const bytes_ostream& other) const {
        return !(*this == other);
    }
};

template<>
struct appending_hash<bytes_ostream> {
    template<typename Hasher>
    void operator()(Hasher& h, const bytes_ostream& b) const {
        for (auto&& frag : b.fragments()) {
            feed_hash(h, frag);
        }
    }
};
<|endoftext|>


<commit_before>#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix_operator.hpp>
#include <boost/spirit/include/qi_parse.hpp>
#include <boost/spirit/include/phoenix_function.hpp>
#include <boost/fusion/include/std_pair.hpp>

#include "parser.h"

Parser::Parser()
{
}

namespace qi = boost::spirit::qi;
namespace ascii = boost::spirit::ascii;
namespace phx = boost::phoenix;

// this function object is used for star'ing elements from within the parser
// the templates achieve, that this can be used for every type (hopefully a semiring)
// TODO: enforce that T is subclass of Semiring!
struct star_impl
{
	template <typename T> struct result { typedef T type; }; // needed because this is how boost knows of what type the result will be

	template <typename T>
	T operator()(T& s) const // this function does the actual work
	{
		return s.star();
	}
};
const phx::function<star_impl> star;

struct free_var_impl
{
	template <typename T>
	struct result { typedef FreeSemiring type; }; // needed because this is how boost knows of what type the result will be

	//template <typename T>
	const FreeSemiring operator()(std::string& s) const // this function does the actual work
	{
		// create an element with the given var
		return FreeSemiring(Var::getVar(s));
	}
};
const phx::function<free_var_impl> free_var;

struct rexp_var_impl
{
	template <typename T>
	struct result { typedef CommutativeRExp type; }; // needed because this is how boost knows of what type the result will be

	//template <typename T>
	const CommutativeRExp operator()(std::string& s) const // this function does the actual work
	{
		// create an element with the given var
		return CommutativeRExp(Var::getVar(s));
	}
};
const phx::function<rexp_var_impl> rexp_var;

struct polyrexp_impl
{
	template <typename T,typename U>
	struct result { typedef Polynomial<CommutativeRExp> type; }; // needed because this is how boost knows of what type the result will be

	const Polynomial<CommutativeRExp> operator()(CommutativeRExp& s, std::vector<std::string>& v) const // this function does the actual work
	{
		// create an element with the given var
		std::vector<VarPtr> vars;
		for(auto it = v.begin(); it != v.end(); ++it)
			vars.push_back(Var::getVar(*it));
		return Polynomial<CommutativeRExp>({Monomial<CommutativeRExp>(s,vars)});
	}
};
const phx::function<polyrexp_impl> polyrexp;

struct polyrexp2_impl
{
	template <typename T>
	struct result { typedef Polynomial<CommutativeRExp> type; }; // needed because this is how boost knows of what type the result will be

	const Polynomial<CommutativeRExp> operator()(std::string& s) const // this function does the actual work
	{
		// create an element without a variable
		return Polynomial<CommutativeRExp>({Monomial<CommutativeRExp>(CommutativeRExp(Var::getVar(s)),{})});
	}
};
const phx::function<polyrexp2_impl> polyrexp2;

struct polyvar_impl
{
	template <typename T>
	struct result { typedef Polynomial<CommutativeRExp> type; }; // needed because this is how boost knows of what type the result will be

	const Polynomial<CommutativeRExp> operator()(std::string& var) const // this function does the actual work
	{
		// create an element with the given var
		if(var == "1") // special case for the one-element
			return Polynomial<CommutativeRExp>::one();
		else
			return Polynomial<CommutativeRExp>({Monomial<CommutativeRExp>(CommutativeRExp::one(),{Var::getVar(var)})});
	}
};
const phx::function<polyvar_impl> polyvar;

// some boost::qi methods for pushing values through our parser
using qi::_val;
using qi::_1;
using qi::_2;
using qi::lit;
using qi::lexeme;
using qi::eps;

template <typename Iterator, typename Skipper>
struct float_parser : qi::grammar<Iterator, FloatSemiring(), Skipper>
{
	// specify the parser rules, term is the start rule (and in this case the only rule)
	float_parser() : float_parser::base_type(expression)
	{
		// what does a float expression look like?
		expression = term [_val  = _1] >> *( '+' >> term [_val = _val + _1] );
		term = starfactor [_val = _1] >> *( 'x' >> starfactor [_val = _val * _1] );
		starfactor = factor [_val = _1] >> -(lit('*'))[_val = star(_val)];
		factor =
			qi::float_ [_val = _1] |
			'(' >> expression [_val = _1] >> ')';
	}

	// this declare the available rules and the respecting return types of our parser
	qi::rule<Iterator, FloatSemiring(), Skipper> expression;
	qi::rule<Iterator, FloatSemiring(), Skipper> term;
	qi::rule<Iterator, FloatSemiring(), Skipper> starfactor;
	qi::rule<Iterator, FloatSemiring(), Skipper> factor;

};

template <typename Iterator, typename Skipper>
struct free_parser : qi::grammar<Iterator, FreeSemiring(), Skipper>
{
	// specify the parser rules, term is the start rule (and in this case the only rule)
	free_parser() : free_parser::base_type(expression)
	{
		// what does a free semiring term look like?
		expression = term [_val  = _1] >> *( '+' >> term [_val = _val + _1] );
		term = starfactor [_val = _1] >> *( '.' >> starfactor [_val = _val * _1] );
		starfactor = factor [_val = _1] >> -(lit('*'))[_val = star(_val)];
		factor =
			qi::as_string[lexeme[+qi::alpha]] [_val = free_var(_1)] |
			'(' >> expression [_val = _1] >> ')';
	}

	// this declare the available rules and the respecting return types of our parser
	qi::rule<Iterator, FreeSemiring(), Skipper> expression;
	qi::rule<Iterator, FreeSemiring(), Skipper> term;
	qi::rule<Iterator, FreeSemiring(), Skipper> starfactor;
	qi::rule<Iterator, FreeSemiring(), Skipper> factor;
};

template <typename Iterator, typename Skipper>
struct rexp_parser : qi::grammar<Iterator, CommutativeRExp(), Skipper>
{
	// specify the parser rules, term is the start rule (and in this case the only rule)
	rexp_parser() : rexp_parser::base_type(expression)
	{
		// what does a commutative regular expression term look like?
		expression = term [_val  = _1] >> *( '+' >> term [_val = _val + _1] );
		term = starfactor [_val = _1] >> *( '.' >> starfactor [_val = _val * _1] );
		starfactor = factor [_val = _1] >> -(lit('*'))[_val = star(_val)];
		factor =
			qi::as_string[lexeme[+qi::alpha]] [_val = rexp_var(_1)] |
			'(' >> expression [_val = _1] >> ')';
	}

	// this declare the available rules and the respecting return types of our parser
	qi::rule<Iterator, CommutativeRExp(), Skipper> expression;
	qi::rule<Iterator, CommutativeRExp(), Skipper> term;
	qi::rule<Iterator, CommutativeRExp(), Skipper> starfactor;
	qi::rule<Iterator, CommutativeRExp(), Skipper> factor;
};

template <typename Iterator, typename Skipper>
struct polyrexp_parser : qi::grammar<Iterator, Polynomial<CommutativeRExp>(), Skipper>
{
	// specify the parser rules, term is the start rule (and in this case the only rule)
	polyrexp_parser() : polyrexp_parser::base_type(expression)
	{
		// what does a polynomial in the commutative regular expression semiring look like?
		expression = term [_val  = _1] >> *( '+' >> term [_val = _val + _1] );
		term = ( rexp >> *( '.' >> var) )[_val = polyrexp(_1, _2)];
		var = +qi::as_string[lexeme[+qi::upper]] [_val = _1];
		//var = +qi::as_string[lexeme['<' >> +(ascii::char_ - '>') >> '>']] [_val = _1];
	}

	// this declare the available rules and the respecting return types of our parser
	qi::rule<Iterator, Polynomial<CommutativeRExp>(), Skipper> expression;
	qi::rule<Iterator, Polynomial<CommutativeRExp>(), Skipper> term;
	qi::rule<Iterator, std::string(), Skipper> var;
	rexp_parser<Iterator, Skipper> rexp;
};

//FIXME: empty lines in the input-file lead to a segfault :)
//TODO: allow rules going over multiple lines

template <typename Iterator, typename Skipper>
struct grammar_parser : qi::grammar<Iterator, std::pair<std::string, Polynomial<CommutativeRExp>>(), Skipper>
{
	// specify the parser rules, term is the start rule (and in this case the only rule)
	grammar_parser() : grammar_parser::base_type(rule)
	{
		// what does a polynomial in the commutative regular expression semiring look like?
		rule %= (varidentifier >> lexeme["::="] >> expression);
		expression = term [_val = _1] >> *('|' >> expression [_val = _val + _1]); // addition
		term = 	eps [_val = Polynomial<CommutativeRExp>::one()] >> // set _val to be the one-element of the semiring
			*(
				('(' >> expression >> ')') [_val = _val * _1] |
				literal	[_val = _val * _1] |
				var	[_val = _val * _1]  ); // multiplication
		//literal = '"' >> literalidentifier [_val = polyrexp2(_1)] >> '"';
		literal = sidentifier [_val = polyrexp2(_1)] |
				'"' >> literalidentifier [_val = polyrexp2(_1)] >> '"';
		var = varidentifier[_val = polyvar(_1)];
		literalidentifier = qi::as_string[lexeme[+(ascii::char_ - '"' - '|' -'('- ')' - ascii::space - '<' - '>')]];
		sidentifier = qi::as_string[lexeme[(ascii::char_ - '"' - '|' -'('- ')' - ascii::space - '<' - '>')]];
		//varidentifier = qi::as_string[lexeme[+(ascii::char_ - ':' - '=' - '|' - '(' - ')' - ascii::space)]];
		varidentifier = qi::as_string[lexeme['<' >> +(ascii::char_ - '>') >> '>']];
	}

	// this declares the available rules and the respecting return types of our parser
	qi::rule<Iterator, std::pair<std::string, Polynomial<CommutativeRExp>>(), Skipper> rule;
	qi::rule<Iterator, Polynomial<CommutativeRExp>(), Skipper> expression;
	qi::rule<Iterator, Polynomial<CommutativeRExp>(), Skipper> term;
	qi::rule<Iterator, Polynomial<CommutativeRExp>(), Skipper> var;
	qi::rule<Iterator, Polynomial<CommutativeRExp>(), Skipper> literal;
	qi::rule<Iterator, std::string(), Skipper> varidentifier;
	qi::rule<Iterator, std::string(), Skipper> literalidentifier;
	qi::rule<Iterator, std::string(), Skipper> sidentifier;
};

typedef std::string::const_iterator iterator_type;

FloatSemiring Parser::parse_float(std::string input)
{
	typedef float_parser<iterator_type, qi::space_type> float_parser;
	float_parser floater;

	iterator_type iter = input.begin();
	iterator_type end = input.end();

	FloatSemiring result;
	if(!(qi::phrase_parse(iter, end, floater, qi::space, result) && iter == end))
		std::cout << "bad input, failed at: " << std::string(iter, end) << std::endl;

	return result;
}

FreeSemiring Parser::parse_free(std::string input)
{
	typedef free_parser<iterator_type, qi::space_type> free_parser;
	free_parser freeer;

	iterator_type iter = input.begin();
	iterator_type end = input.end();

	FreeSemiring result;
	if(!(qi::phrase_parse(iter, end, freeer, qi::space, result) && iter == end))
		std::cout << "bad input, failed at: " << std::string(iter, end) << std::endl;

	return result;
}

CommutativeRExp Parser::parse_rexp(std::string input)
{
	typedef rexp_parser<iterator_type, qi::space_type> rexp_parser;
	rexp_parser rexper;

	iterator_type iter = input.begin();
	iterator_type end = input.end();

	CommutativeRExp result;
	if(!(qi::phrase_parse(iter, end, rexper, qi::space, result) && iter == end))
		std::cout << "bad input, failed at: " << std::string(iter, end) << std::endl;

	return result;
}

Polynomial<CommutativeRExp> Parser::parse_polyrexp(std::string input)
{
	typedef polyrexp_parser<iterator_type, qi::space_type> polyrexp_parser;
	polyrexp_parser polyrexper;

	iterator_type iter = input.begin();
	iterator_type end = input.end();

	Polynomial<CommutativeRExp> result;
	if(!(qi::phrase_parse(iter, end, polyrexper, qi::space, result) && iter == end))
		std::cout << "bad input, failed at: " << std::string(iter, end) << std::endl;

	return result;
}

std::pair<VarPtr, Polynomial<CommutativeRExp>> Parser::parse_grammar(std::string input)
{
	typedef grammar_parser<iterator_type, qi::space_type> grammar_parser;
	grammar_parser grammarer;

	iterator_type iter = input.begin();
	iterator_type end = input.end();

	std::pair<std::string, Polynomial<CommutativeRExp>> result;
	if(!(qi::phrase_parse(iter, end, grammarer, qi::space, result) && iter == end))
		std::cout << "bad input, failed at: " << std::string(iter, end) << std::endl;

	return std::pair<VarPtr, Polynomial<CommutativeRExp>>(Var::getVar(result.first), result.second);
}
<commit_msg>fixed small parser-bug... literals may be multiple-char literals enclosed in "..." or single char lits<commit_after>#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix_operator.hpp>
#include <boost/spirit/include/qi_parse.hpp>
#include <boost/spirit/include/phoenix_function.hpp>
#include <boost/fusion/include/std_pair.hpp>

#include "parser.h"

Parser::Parser()
{
}

namespace qi = boost::spirit::qi;
namespace ascii = boost::spirit::ascii;
namespace phx = boost::phoenix;

// this function object is used for star'ing elements from within the parser
// the templates achieve, that this can be used for every type (hopefully a semiring)
// TODO: enforce that T is subclass of Semiring!
struct star_impl
{
	template <typename T> struct result { typedef T type; }; // needed because this is how boost knows of what type the result will be

	template <typename T>
	T operator()(T& s) const // this function does the actual work
	{
		return s.star();
	}
};
const phx::function<star_impl> star;

struct free_var_impl
{
	template <typename T>
	struct result { typedef FreeSemiring type; }; // needed because this is how boost knows of what type the result will be

	//template <typename T>
	const FreeSemiring operator()(std::string& s) const // this function does the actual work
	{
		// create an element with the given var
		return FreeSemiring(Var::getVar(s));
	}
};
const phx::function<free_var_impl> free_var;

struct rexp_var_impl
{
	template <typename T>
	struct result { typedef CommutativeRExp type; }; // needed because this is how boost knows of what type the result will be

	//template <typename T>
	const CommutativeRExp operator()(std::string& s) const // this function does the actual work
	{
		// create an element with the given var
		return CommutativeRExp(Var::getVar(s));
	}
};
const phx::function<rexp_var_impl> rexp_var;

struct polyrexp_impl
{
	template <typename T,typename U>
	struct result { typedef Polynomial<CommutativeRExp> type; }; // needed because this is how boost knows of what type the result will be

	const Polynomial<CommutativeRExp> operator()(CommutativeRExp& s, std::vector<std::string>& v) const // this function does the actual work
	{
		// create an element with the given var
		std::vector<VarPtr> vars;
		for(auto it = v.begin(); it != v.end(); ++it)
			vars.push_back(Var::getVar(*it));
		return Polynomial<CommutativeRExp>({Monomial<CommutativeRExp>(s,vars)});
	}
};
const phx::function<polyrexp_impl> polyrexp;

struct polyrexp2_impl
{
	template <typename T>
	struct result { typedef Polynomial<CommutativeRExp> type; }; // needed because this is how boost knows of what type the result will be

	const Polynomial<CommutativeRExp> operator()(std::string& s) const // this function does the actual work
	{
		// create an element without a variable
		return Polynomial<CommutativeRExp>({Monomial<CommutativeRExp>(CommutativeRExp(Var::getVar(s)),{})});
	}
};
const phx::function<polyrexp2_impl> polyrexp2;

struct polyvar_impl
{
	template <typename T>
	struct result { typedef Polynomial<CommutativeRExp> type; }; // needed because this is how boost knows of what type the result will be

	const Polynomial<CommutativeRExp> operator()(std::string& var) const // this function does the actual work
	{
		// create an element with the given var
		if(var == "1") // special case for the one-element
			return Polynomial<CommutativeRExp>::one();
		else
			return Polynomial<CommutativeRExp>({Monomial<CommutativeRExp>(CommutativeRExp::one(),{Var::getVar(var)})});
	}
};
const phx::function<polyvar_impl> polyvar;

// some boost::qi methods for pushing values through our parser
using qi::_val;
using qi::_1;
using qi::_2;
using qi::lit;
using qi::lexeme;
using qi::eps;

template <typename Iterator, typename Skipper>
struct float_parser : qi::grammar<Iterator, FloatSemiring(), Skipper>
{
	// specify the parser rules, term is the start rule (and in this case the only rule)
	float_parser() : float_parser::base_type(expression)
	{
		// what does a float expression look like?
		expression = term [_val  = _1] >> *( '+' >> term [_val = _val + _1] );
		term = starfactor [_val = _1] >> *( 'x' >> starfactor [_val = _val * _1] );
		starfactor = factor [_val = _1] >> -(lit('*'))[_val = star(_val)];
		factor =
			qi::float_ [_val = _1] |
			'(' >> expression [_val = _1] >> ')';
	}

	// this declare the available rules and the respecting return types of our parser
	qi::rule<Iterator, FloatSemiring(), Skipper> expression;
	qi::rule<Iterator, FloatSemiring(), Skipper> term;
	qi::rule<Iterator, FloatSemiring(), Skipper> starfactor;
	qi::rule<Iterator, FloatSemiring(), Skipper> factor;

};

template <typename Iterator, typename Skipper>
struct free_parser : qi::grammar<Iterator, FreeSemiring(), Skipper>
{
	// specify the parser rules, term is the start rule (and in this case the only rule)
	free_parser() : free_parser::base_type(expression)
	{
		// what does a free semiring term look like?
		expression = term [_val  = _1] >> *( '+' >> term [_val = _val + _1] );
		term = starfactor [_val = _1] >> *( '.' >> starfactor [_val = _val * _1] );
		starfactor = factor [_val = _1] >> -(lit('*'))[_val = star(_val)];
		factor =
			qi::as_string[lexeme[+qi::alpha]] [_val = free_var(_1)] |
			'(' >> expression [_val = _1] >> ')';
	}

	// this declare the available rules and the respecting return types of our parser
	qi::rule<Iterator, FreeSemiring(), Skipper> expression;
	qi::rule<Iterator, FreeSemiring(), Skipper> term;
	qi::rule<Iterator, FreeSemiring(), Skipper> starfactor;
	qi::rule<Iterator, FreeSemiring(), Skipper> factor;
};

template <typename Iterator, typename Skipper>
struct rexp_parser : qi::grammar<Iterator, CommutativeRExp(), Skipper>
{
	// specify the parser rules, term is the start rule (and in this case the only rule)
	rexp_parser() : rexp_parser::base_type(expression)
	{
		// what does a commutative regular expression term look like?
		expression = term [_val  = _1] >> *( '+' >> term [_val = _val + _1] );
		term = starfactor [_val = _1] >> *( '.' >> starfactor [_val = _val * _1] );
		starfactor = factor [_val = _1] >> -(lit('*'))[_val = star(_val)];
		factor =
			qi::as_string[lexeme[+qi::alpha]] [_val = rexp_var(_1)] |
			'(' >> expression [_val = _1] >> ')';
	}

	// this declare the available rules and the respecting return types of our parser
	qi::rule<Iterator, CommutativeRExp(), Skipper> expression;
	qi::rule<Iterator, CommutativeRExp(), Skipper> term;
	qi::rule<Iterator, CommutativeRExp(), Skipper> starfactor;
	qi::rule<Iterator, CommutativeRExp(), Skipper> factor;
};

template <typename Iterator, typename Skipper>
struct polyrexp_parser : qi::grammar<Iterator, Polynomial<CommutativeRExp>(), Skipper>
{
	// specify the parser rules, term is the start rule (and in this case the only rule)
	polyrexp_parser() : polyrexp_parser::base_type(expression)
	{
		// what does a polynomial in the commutative regular expression semiring look like?
		expression = term [_val  = _1] >> *( '+' >> term [_val = _val + _1] );
		term = ( rexp >> *( '.' >> var) )[_val = polyrexp(_1, _2)];
		var = +qi::as_string[lexeme[+qi::upper]] [_val = _1];
		//var = +qi::as_string[lexeme['<' >> +(ascii::char_ - '>') >> '>']] [_val = _1];
	}

	// this declare the available rules and the respecting return types of our parser
	qi::rule<Iterator, Polynomial<CommutativeRExp>(), Skipper> expression;
	qi::rule<Iterator, Polynomial<CommutativeRExp>(), Skipper> term;
	qi::rule<Iterator, std::string(), Skipper> var;
	rexp_parser<Iterator, Skipper> rexp;
};

//FIXME: empty lines in the input-file lead to a segfault :)
//TODO: allow rules going over multiple lines

template <typename Iterator, typename Skipper>
struct grammar_parser : qi::grammar<Iterator, std::pair<std::string, Polynomial<CommutativeRExp>>(), Skipper>
{
	// specify the parser rules, term is the start rule (and in this case the only rule)
	grammar_parser() : grammar_parser::base_type(rule)
	{
		// what does a polynomial in the commutative regular expression semiring look like?
		rule %= (varidentifier >> lexeme["::="] >> expression);
		expression = term [_val = _1] >> *('|' >> expression [_val = _val + _1]); // addition
		term = 	eps [_val = Polynomial<CommutativeRExp>::one()] >> // set _val to be the one-element of the semiring
			*(
				('(' >> expression >> ')') [_val = _val * _1] |
				var	[_val = _val * _1]   | // multiplication
                literal	[_val = _val * _1] );
		//literal = '"' >> literalidentifier [_val = polyrexp2(_1)] >> '"';
		literal = '"' >> literalidentifier [_val = polyrexp2(_1)] >> '"' | sidentifier [_val = polyrexp2(_1)];

		var = varidentifier[_val = polyvar(_1)];
		literalidentifier = qi::as_string[lexeme[+(ascii::char_ -'"')] ];
		sidentifier = qi::as_string[lexeme[(ascii::char_ - '"' - '|' -'('- ')' - ascii::space - '<' - '>')]];
		//varidentifier = qi::as_string[lexeme[+(ascii::char_ - ':' - '=' - '|' - '(' - ')' - ascii::space)]];
		varidentifier = qi::as_string[lexeme['<' >> +(ascii::char_ - '>') >> '>']];
	}

	// this declares the available rules and the respecting return types of our parser
	qi::rule<Iterator, std::pair<std::string, Polynomial<CommutativeRExp>>(), Skipper> rule;
	qi::rule<Iterator, Polynomial<CommutativeRExp>(), Skipper> expression;
	qi::rule<Iterator, Polynomial<CommutativeRExp>(), Skipper> term;
	qi::rule<Iterator, Polynomial<CommutativeRExp>(), Skipper> var;
	qi::rule<Iterator, Polynomial<CommutativeRExp>(), Skipper> literal;
	qi::rule<Iterator, std::string(), Skipper> varidentifier;
	qi::rule<Iterator, std::string(), Skipper> literalidentifier;
	qi::rule<Iterator, std::string(), Skipper> sidentifier;
};

typedef std::string::const_iterator iterator_type;

FloatSemiring Parser::parse_float(std::string input)
{
	typedef float_parser<iterator_type, qi::space_type> float_parser;
	float_parser floater;

	iterator_type iter = input.begin();
	iterator_type end = input.end();

	FloatSemiring result;
	if(!(qi::phrase_parse(iter, end, floater, qi::space, result) && iter == end))
		std::cout << "bad input, failed at: " << std::string(iter, end) << std::endl;

	return result;
}

FreeSemiring Parser::parse_free(std::string input)
{
	typedef free_parser<iterator_type, qi::space_type> free_parser;
	free_parser freeer;

	iterator_type iter = input.begin();
	iterator_type end = input.end();

	FreeSemiring result;
	if(!(qi::phrase_parse(iter, end, freeer, qi::space, result) && iter == end))
		std::cout << "bad input, failed at: " << std::string(iter, end) << std::endl;

	return result;
}

CommutativeRExp Parser::parse_rexp(std::string input)
{
	typedef rexp_parser<iterator_type, qi::space_type> rexp_parser;
	rexp_parser rexper;

	iterator_type iter = input.begin();
	iterator_type end = input.end();

	CommutativeRExp result;
	if(!(qi::phrase_parse(iter, end, rexper, qi::space, result) && iter == end))
		std::cout << "bad input, failed at: " << std::string(iter, end) << std::endl;

	return result;
}

Polynomial<CommutativeRExp> Parser::parse_polyrexp(std::string input)
{
	typedef polyrexp_parser<iterator_type, qi::space_type> polyrexp_parser;
	polyrexp_parser polyrexper;

	iterator_type iter = input.begin();
	iterator_type end = input.end();

	Polynomial<CommutativeRExp> result;
	if(!(qi::phrase_parse(iter, end, polyrexper, qi::space, result) && iter == end))
		std::cout << "bad input, failed at: " << std::string(iter, end) << std::endl;

	return result;
}

std::pair<VarPtr, Polynomial<CommutativeRExp>> Parser::parse_grammar(std::string input)
{
	typedef grammar_parser<iterator_type, qi::space_type> grammar_parser;
	grammar_parser grammarer;

	iterator_type iter = input.begin();
	iterator_type end = input.end();

	std::pair<std::string, Polynomial<CommutativeRExp>> result;
	if(!(qi::phrase_parse(iter, end, grammarer, qi::space, result) && iter == end))
		std::cout << "bad input, failed at: " << std::string(iter, end) << std::endl;

	return std::pair<VarPtr, Polynomial<CommutativeRExp>>(Var::getVar(result.first), result.second);
}
<|endoftext|>