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

#include <cstdint> // unint8_t etc
#include <cstdlib>
#include <cstring>
#include <climits>

#ifdef REALM_DEBUG
    #include <iostream>
    #include <iomanip>
    #include <sstream>
#endif

#include <realm/column.hpp>
#include <realm/column_table.hpp>
#include <realm/column_mixed.hpp>
#include <realm/query_engine.hpp>
#include <realm/exceptions.hpp>
#include <realm/table.hpp>
#include <realm/index_string.hpp>
#include <realm/array_integer.hpp>

using namespace realm;
using namespace realm::util;

bool ColumnBase::is_nullable() const noexcept
{
    return false;
}

bool ColumnBase::is_null(size_t) const noexcept
{
    return false;
}

void ColumnBase::set_null(size_t)
{
    throw LogicError{LogicError::column_not_nullable};
}

void ColumnBase::move_assign(ColumnBase&) noexcept
{
    destroy();
}

void ColumnBase::refresh_accessor_tree(size_t new_col_ndx, const realm::Spec&)
{
    m_column_ndx = new_col_ndx;
}

void ColumnBaseWithIndex::move_assign(ColumnBaseWithIndex& col) noexcept
{
    ColumnBase::move_assign(col);
    m_search_index = std::move(col.m_search_index);
}

void ColumnBase::set_string(size_t, StringData)
{
    throw LogicError(LogicError::type_mismatch);
}

void ColumnBaseWithIndex::set_ndx_in_parent(size_t ndx) noexcept
{
    if (m_search_index) {
        m_search_index->set_ndx_in_parent(ndx + 1);
    }
}

void ColumnBaseWithIndex::update_from_parent(size_t old_baseline) noexcept
{
    if (m_search_index) {
        m_search_index->update_from_parent(old_baseline);
    }
}

void ColumnBaseWithIndex::refresh_accessor_tree(size_t new_col_ndx, const realm::Spec& spec)
{
    ColumnBase::refresh_accessor_tree(new_col_ndx, spec);
    if (m_search_index) {
        m_search_index->refresh_accessor_tree(new_col_ndx, spec);
    }
}

void ColumnBase::cascade_break_backlinks_to(size_t, CascadeState&)
{
    // No-op by default
}

void ColumnBase::cascade_break_backlinks_to_all_rows(size_t, CascadeState&)
{
    // No-op by default
}

void ColumnBaseWithIndex::destroy() noexcept
{
    if (m_search_index) {
        m_search_index->destroy();
    }
}

void ColumnBase::verify(const Table&, size_t column_ndx) const
{
    verify();
    REALM_ASSERT_EX(column_ndx == m_column_ndx, column_ndx, m_column_ndx);
}

void ColumnBaseSimple::replace_root_array(std::unique_ptr<Array> leaf)
{
    // FIXME: Duplicated from bptree.cpp.
    ArrayParent* parent = m_array->get_parent();
    size_t ndx_in_parent = m_array->get_ndx_in_parent();
    leaf->set_parent(parent, ndx_in_parent);
    leaf->update_parent(); // Throws
    m_array = std::move(leaf);
}

namespace {

struct GetSizeFromRef {
    const ref_type m_ref;
    Allocator& m_alloc;
    size_t m_size;
    GetSizeFromRef(ref_type r, Allocator& a): m_ref(r), m_alloc(a), m_size(0) {}

    template<class Col>
    void call() noexcept
    {
        m_size = Col::get_size_from_ref(m_ref, m_alloc);
    }
};

template<class Op>
void col_type_deleg(Op& op, ColumnType type, bool nullable)
{
    switch (type) {
        case col_type_Int:
        case col_type_Bool:
        case col_type_OldDateTime:
        case col_type_Link:
            if(nullable)
                op.template call<IntNullColumn>();
            else
                op.template call<IntegerColumn>();
            return;
        case col_type_Timestamp:
            op.template call<TimestampColumn>();
            return;
        case col_type_String:
            op.template call<StringColumn>();
            return;
        case col_type_StringEnum:
            op.template call<StringEnumColumn>();
            return;
        case col_type_Binary:
            op.template call<BinaryColumn>();
            return;
        case col_type_Table:
            op.template call<SubtableColumn>();
            return;
        case col_type_Mixed:
            op.template call<MixedColumn>();
            return;
        case col_type_Float:
            op.template call<FloatColumn>();
            return;
        case col_type_Double:
            op.template call<DoubleColumn>();
            return;
        case col_type_LinkList:
            op.template call<LinkListColumn>();
            return;
        case col_type_BackLink:
            op.template call<BacklinkColumn>();
            return;
        case col_type_Reserved4:
            break;
    }
    REALM_ASSERT_DEBUG(false);
}

} // anonymous namespace

size_t ColumnBase::get_size_from_type_and_ref(ColumnType type, ref_type ref,
                                              Allocator& alloc,
                                              bool nullable) noexcept
{
    GetSizeFromRef op(ref, alloc);
    col_type_deleg(op, type, nullable);
    return op.m_size;
}

ref_type ColumnBaseSimple::write(const Array* root, size_t slice_offset, size_t slice_size,
                                 size_t table_size, SliceHandler& handler, _impl::OutputStream& out)
{
    return BpTreeBase::write_subtree(*root, slice_offset, slice_size, table_size, handler, out);
}

void ColumnBaseSimple::introduce_new_root(ref_type new_sibling_ref, Array::TreeInsertBase& state,
                                          bool is_append)
{
    // At this point the original root and its new sibling is either
    // both leaves, or both inner nodes on the same form, compact or
    // general. Due to invar:bptree-node-form, the new root is allowed
    // to be on the compact form if is_append is true and both
    // siblings are either leaves or inner nodes on the compact form.

    Array* orig_root = get_root_array();
    Allocator& alloc = get_alloc();
    std::unique_ptr<Array> new_root(new Array(alloc)); // Throws
    new_root->create(Array::type_InnerBptreeNode); // Throws
    new_root->set_parent(orig_root->get_parent(), orig_root->get_ndx_in_parent());
    new_root->update_parent(); // Throws
    bool compact_form =
        is_append && (!orig_root->is_inner_bptree_node() || orig_root->get(0) % 2 != 0);
    // Something is wrong if we were not appending and the original
    // root is still on the compact form.
    REALM_ASSERT(!compact_form || is_append);
    if (compact_form) {
        int_fast64_t v = to_int64(state.m_split_offset); // elems_per_child
        new_root->add(1 + 2 * v); // Throws
    }
    else {
        Array new_offsets(alloc);
        new_offsets.create(Array::type_Normal); // Throws
        new_offsets.add(to_int64(state.m_split_offset)); // Throws
        new_root->add(from_ref(new_offsets.get_ref()));  // Throws
    }
    new_root->add(from_ref(orig_root->get_ref())); // Throws
    new_root->add(from_ref(new_sibling_ref));      // Throws
    int_fast64_t v = to_int64(state.m_split_size); // total_elems_in_tree
    new_root->add(1 + 2 * v); // Throws
    replace_root_array(std::move(new_root));
}

ref_type ColumnBase::build(size_t* rest_size_ptr, size_t fixed_height,
                           Allocator& alloc, CreateHandler& handler)
{
    size_t rest_size = *rest_size_ptr;
    size_t orig_rest_size = rest_size;
    size_t elems_per_child = REALM_MAX_BPNODE_SIZE;
    size_t leaf_size = std::min(elems_per_child, rest_size);
    rest_size -= leaf_size;
    ref_type node = handler.create_leaf(leaf_size);
    size_t height = 1;
    try {
        for (;;) {
            if (fixed_height > 0 ? fixed_height == height : rest_size == 0) {
                *rest_size_ptr = rest_size;
                return node;
            }
            Array new_inner_node(alloc);
            new_inner_node.create(Array::type_InnerBptreeNode); // Throws
            try {
                int_fast64_t v = elems_per_child;
                new_inner_node.add(1 + 2 * v); // Throws
                v = from_ref(node);
                new_inner_node.add(v); // Throws
                node = 0;
                size_t num_children = 1;
                while (rest_size > 0 && num_children != REALM_MAX_BPNODE_SIZE) {
                    ref_type child = build(&rest_size, height, alloc, handler); // Throws
                    try {
                        int_fast64_t w = from_ref(child);
                        new_inner_node.add(w); // Throws
                    }
                    // LCOV_EXCL_START
                    catch (...) {
                        Array::destroy_deep(child, alloc);
                        throw;
                    }
                    // LCOV_EXCL_END
                    ++num_children;
                }
                v = orig_rest_size - rest_size; // total_elems_in_tree
                new_inner_node.add(1 + 2 * v); // Throws
            }
            // LCOV_EXCL_START
            catch (...) {
                new_inner_node.destroy_deep();
                throw;
            }
            // LCOV_EXCL_END
            node = new_inner_node.get_ref();
            ++height;
            // Overflow is impossible here is all nodes will have elems_per_child <= orig_rest_size
            elems_per_child *= REALM_MAX_BPNODE_SIZE;
        }
    }
    // LCOV_EXCL_START
    catch (...) {
        if (node != 0)
            Array::destroy_deep(node, alloc);
        throw;
    }
    // LCOV_EXCL_END
}

/*
// TODO: Set owner of created arrays and destroy/delete them if created by merge_references()
void IntegerColumn::reference_sort(size_t start, size_t end, Column& ref)
{
    Array values; // pointers to non-instantiated arrays of values
    Array indexes; // pointers to instantiated arrays of index pointers
    Array all_values;
    TreeVisitLeafs<Array, IntegerColumn>(start, end, 0, callme_arrays, &values);

    size_t offset = 0;
    for (size_t t = 0; t < values.size(); t++) {
        Array* i = new Array();
        ref_type ref = values.get_as_ref(t);
        Array v(ref);
        for (size_t j = 0; j < v.size(); j++)
            all_values.add(v.get(j));
        v.reference_sort(*i);
        for (size_t n = 0; n < v.size(); n++)
            i->set(n, i->get(n) + offset);
        offset += v.size();
        indexes.add(int64_t(i));
    }

    Array* ResI;

    merge_references(&all_values, &indexes, &ResI);

    for (size_t t = 0; t < ResI->size(); t++)
        ref.add(ResI->get(t));
}
*/

void ColumnBaseWithIndex::destroy_search_index() noexcept
{
    m_search_index.reset();
}

void ColumnBaseWithIndex::set_search_index_ref(ref_type ref, ArrayParent* parent,
                                               size_t ndx_in_parent, bool allow_duplicate_valaues)
{
    REALM_ASSERT(!m_search_index);
    m_search_index.reset(new StringIndex(ref, parent, ndx_in_parent, this,
                                         !allow_duplicate_valaues, get_alloc())); // Throws
}

#ifdef REALM_DEBUG  // LCOV_EXCL_START ignore debug functions

class ColumnBase::LeafToDot: public Array::ToDotHandler {
public:
    const ColumnBase& m_column;
    LeafToDot(const ColumnBase& column): m_column(column) {}
    void to_dot(MemRef mem, ArrayParent* parent, size_t ndx_in_parent,
                std::ostream& out) override
    {
        m_column.leaf_to_dot(mem, parent, ndx_in_parent, out);
    }
};

void ColumnBaseSimple::tree_to_dot(std::ostream& out) const
{
    ColumnBase::bptree_to_dot(get_root_array(), out);
}

void ColumnBase::bptree_to_dot(const Array* root, std::ostream& out) const
{
    LeafToDot handler(*this);
    root->bptree_to_dot(out, handler);
}

void ColumnBase::dump_node_structure() const
{
    do_dump_node_structure(std::cerr, 0);
}

namespace realm {
namespace _impl {

void leaf_dumper(MemRef mem, Allocator& alloc, std::ostream& out, int level)
{
    Array leaf(alloc);
    leaf.init_from_mem(mem);
    int indent = level * 2;
    out << std::setw(indent) << "" << "Integer leaf (ref: " << leaf.get_ref() << ", "
        "size: " << leaf.size() << ")\n";
    std::ostringstream out_2;
    for (size_t i = 0; i != leaf.size(); ++i) {
        if (i != 0) {
            out_2 << ", ";
            if (out_2.tellp() > 70) {
                out_2 << "...";
                break;
            }
        }
        out_2 << leaf.get(i);
    }
    out << std::setw(indent) << "" << "  Elems: " << out_2.str() << "\n";
}

} // namespace _impl
} // namespace realm

#endif // LCOV_EXCL_STOP ignore debug functions
<commit_msg>Fixed a few LCOV_EXCL_END -> LCOV_EXCL_STOP<commit_after>/*************************************************************************
 *
 * Copyright 2016 Realm Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 **************************************************************************/

#include <cstdint> // unint8_t etc
#include <cstdlib>
#include <cstring>
#include <climits>

#ifdef REALM_DEBUG
    #include <iostream>
    #include <iomanip>
    #include <sstream>
#endif

#include <realm/column.hpp>
#include <realm/column_table.hpp>
#include <realm/column_mixed.hpp>
#include <realm/query_engine.hpp>
#include <realm/exceptions.hpp>
#include <realm/table.hpp>
#include <realm/index_string.hpp>
#include <realm/array_integer.hpp>

using namespace realm;
using namespace realm::util;

bool ColumnBase::is_nullable() const noexcept
{
    return false;
}

bool ColumnBase::is_null(size_t) const noexcept
{
    return false;
}

void ColumnBase::set_null(size_t)
{
    throw LogicError{LogicError::column_not_nullable};
}

void ColumnBase::move_assign(ColumnBase&) noexcept
{
    destroy();
}

void ColumnBase::refresh_accessor_tree(size_t new_col_ndx, const realm::Spec&)
{
    m_column_ndx = new_col_ndx;
}

void ColumnBaseWithIndex::move_assign(ColumnBaseWithIndex& col) noexcept
{
    ColumnBase::move_assign(col);
    m_search_index = std::move(col.m_search_index);
}

void ColumnBase::set_string(size_t, StringData)
{
    throw LogicError(LogicError::type_mismatch);
}

void ColumnBaseWithIndex::set_ndx_in_parent(size_t ndx) noexcept
{
    if (m_search_index) {
        m_search_index->set_ndx_in_parent(ndx + 1);
    }
}

void ColumnBaseWithIndex::update_from_parent(size_t old_baseline) noexcept
{
    if (m_search_index) {
        m_search_index->update_from_parent(old_baseline);
    }
}

void ColumnBaseWithIndex::refresh_accessor_tree(size_t new_col_ndx, const realm::Spec& spec)
{
    ColumnBase::refresh_accessor_tree(new_col_ndx, spec);
    if (m_search_index) {
        m_search_index->refresh_accessor_tree(new_col_ndx, spec);
    }
}

void ColumnBase::cascade_break_backlinks_to(size_t, CascadeState&)
{
    // No-op by default
}

void ColumnBase::cascade_break_backlinks_to_all_rows(size_t, CascadeState&)
{
    // No-op by default
}

void ColumnBaseWithIndex::destroy() noexcept
{
    if (m_search_index) {
        m_search_index->destroy();
    }
}

void ColumnBase::verify(const Table&, size_t column_ndx) const
{
    verify();
    REALM_ASSERT_EX(column_ndx == m_column_ndx, column_ndx, m_column_ndx);
}

void ColumnBaseSimple::replace_root_array(std::unique_ptr<Array> leaf)
{
    // FIXME: Duplicated from bptree.cpp.
    ArrayParent* parent = m_array->get_parent();
    size_t ndx_in_parent = m_array->get_ndx_in_parent();
    leaf->set_parent(parent, ndx_in_parent);
    leaf->update_parent(); // Throws
    m_array = std::move(leaf);
}

namespace {

struct GetSizeFromRef {
    const ref_type m_ref;
    Allocator& m_alloc;
    size_t m_size;
    GetSizeFromRef(ref_type r, Allocator& a): m_ref(r), m_alloc(a), m_size(0) {}

    template<class Col>
    void call() noexcept
    {
        m_size = Col::get_size_from_ref(m_ref, m_alloc);
    }
};

template<class Op>
void col_type_deleg(Op& op, ColumnType type, bool nullable)
{
    switch (type) {
        case col_type_Int:
        case col_type_Bool:
        case col_type_OldDateTime:
        case col_type_Link:
            if(nullable)
                op.template call<IntNullColumn>();
            else
                op.template call<IntegerColumn>();
            return;
        case col_type_Timestamp:
            op.template call<TimestampColumn>();
            return;
        case col_type_String:
            op.template call<StringColumn>();
            return;
        case col_type_StringEnum:
            op.template call<StringEnumColumn>();
            return;
        case col_type_Binary:
            op.template call<BinaryColumn>();
            return;
        case col_type_Table:
            op.template call<SubtableColumn>();
            return;
        case col_type_Mixed:
            op.template call<MixedColumn>();
            return;
        case col_type_Float:
            op.template call<FloatColumn>();
            return;
        case col_type_Double:
            op.template call<DoubleColumn>();
            return;
        case col_type_LinkList:
            op.template call<LinkListColumn>();
            return;
        case col_type_BackLink:
            op.template call<BacklinkColumn>();
            return;
        case col_type_Reserved4:
            break;
    }
    REALM_ASSERT_DEBUG(false);
}

} // anonymous namespace

size_t ColumnBase::get_size_from_type_and_ref(ColumnType type, ref_type ref,
                                              Allocator& alloc,
                                              bool nullable) noexcept
{
    GetSizeFromRef op(ref, alloc);
    col_type_deleg(op, type, nullable);
    return op.m_size;
}

ref_type ColumnBaseSimple::write(const Array* root, size_t slice_offset, size_t slice_size,
                                 size_t table_size, SliceHandler& handler, _impl::OutputStream& out)
{
    return BpTreeBase::write_subtree(*root, slice_offset, slice_size, table_size, handler, out);
}

void ColumnBaseSimple::introduce_new_root(ref_type new_sibling_ref, Array::TreeInsertBase& state,
                                          bool is_append)
{
    // At this point the original root and its new sibling is either
    // both leaves, or both inner nodes on the same form, compact or
    // general. Due to invar:bptree-node-form, the new root is allowed
    // to be on the compact form if is_append is true and both
    // siblings are either leaves or inner nodes on the compact form.

    Array* orig_root = get_root_array();
    Allocator& alloc = get_alloc();
    std::unique_ptr<Array> new_root(new Array(alloc)); // Throws
    new_root->create(Array::type_InnerBptreeNode); // Throws
    new_root->set_parent(orig_root->get_parent(), orig_root->get_ndx_in_parent());
    new_root->update_parent(); // Throws
    bool compact_form =
        is_append && (!orig_root->is_inner_bptree_node() || orig_root->get(0) % 2 != 0);
    // Something is wrong if we were not appending and the original
    // root is still on the compact form.
    REALM_ASSERT(!compact_form || is_append);
    if (compact_form) {
        int_fast64_t v = to_int64(state.m_split_offset); // elems_per_child
        new_root->add(1 + 2 * v); // Throws
    }
    else {
        Array new_offsets(alloc);
        new_offsets.create(Array::type_Normal); // Throws
        new_offsets.add(to_int64(state.m_split_offset)); // Throws
        new_root->add(from_ref(new_offsets.get_ref()));  // Throws
    }
    new_root->add(from_ref(orig_root->get_ref())); // Throws
    new_root->add(from_ref(new_sibling_ref));      // Throws
    int_fast64_t v = to_int64(state.m_split_size); // total_elems_in_tree
    new_root->add(1 + 2 * v); // Throws
    replace_root_array(std::move(new_root));
}

ref_type ColumnBase::build(size_t* rest_size_ptr, size_t fixed_height,
                           Allocator& alloc, CreateHandler& handler)
{
    size_t rest_size = *rest_size_ptr;
    size_t orig_rest_size = rest_size;
    size_t elems_per_child = REALM_MAX_BPNODE_SIZE;
    size_t leaf_size = std::min(elems_per_child, rest_size);
    rest_size -= leaf_size;
    ref_type node = handler.create_leaf(leaf_size);
    size_t height = 1;
    try {
        for (;;) {
            if (fixed_height > 0 ? fixed_height == height : rest_size == 0) {
                *rest_size_ptr = rest_size;
                return node;
            }
            Array new_inner_node(alloc);
            new_inner_node.create(Array::type_InnerBptreeNode); // Throws
            try {
                int_fast64_t v = elems_per_child;
                new_inner_node.add(1 + 2 * v); // Throws
                v = from_ref(node);
                new_inner_node.add(v); // Throws
                node = 0;
                size_t num_children = 1;
                while (rest_size > 0 && num_children != REALM_MAX_BPNODE_SIZE) {
                    ref_type child = build(&rest_size, height, alloc, handler); // Throws
                    try {
                        int_fast64_t w = from_ref(child);
                        new_inner_node.add(w); // Throws
                    }
                    // LCOV_EXCL_START
                    catch (...) {
                        Array::destroy_deep(child, alloc);
                        throw;
                    }
                    // LCOV_EXCL_STOP
                    ++num_children;
                }
                v = orig_rest_size - rest_size; // total_elems_in_tree
                new_inner_node.add(1 + 2 * v); // Throws
            }
            // LCOV_EXCL_START
            catch (...) {
                new_inner_node.destroy_deep();
                throw;
            }
            // LCOV_EXCL_STOP
            node = new_inner_node.get_ref();
            ++height;
            // Overflow is impossible here is all nodes will have elems_per_child <= orig_rest_size
            elems_per_child *= REALM_MAX_BPNODE_SIZE;
        }
    }
    // LCOV_EXCL_START
    catch (...) {
        if (node != 0)
            Array::destroy_deep(node, alloc);
        throw;
    }
    // LCOV_EXCL_STOP
}

/*
// TODO: Set owner of created arrays and destroy/delete them if created by merge_references()
void IntegerColumn::reference_sort(size_t start, size_t end, Column& ref)
{
    Array values; // pointers to non-instantiated arrays of values
    Array indexes; // pointers to instantiated arrays of index pointers
    Array all_values;
    TreeVisitLeafs<Array, IntegerColumn>(start, end, 0, callme_arrays, &values);

    size_t offset = 0;
    for (size_t t = 0; t < values.size(); t++) {
        Array* i = new Array();
        ref_type ref = values.get_as_ref(t);
        Array v(ref);
        for (size_t j = 0; j < v.size(); j++)
            all_values.add(v.get(j));
        v.reference_sort(*i);
        for (size_t n = 0; n < v.size(); n++)
            i->set(n, i->get(n) + offset);
        offset += v.size();
        indexes.add(int64_t(i));
    }

    Array* ResI;

    merge_references(&all_values, &indexes, &ResI);

    for (size_t t = 0; t < ResI->size(); t++)
        ref.add(ResI->get(t));
}
*/

void ColumnBaseWithIndex::destroy_search_index() noexcept
{
    m_search_index.reset();
}

void ColumnBaseWithIndex::set_search_index_ref(ref_type ref, ArrayParent* parent,
                                               size_t ndx_in_parent, bool allow_duplicate_valaues)
{
    REALM_ASSERT(!m_search_index);
    m_search_index.reset(new StringIndex(ref, parent, ndx_in_parent, this,
                                         !allow_duplicate_valaues, get_alloc())); // Throws
}

#ifdef REALM_DEBUG  // LCOV_EXCL_START ignore debug functions

class ColumnBase::LeafToDot: public Array::ToDotHandler {
public:
    const ColumnBase& m_column;
    LeafToDot(const ColumnBase& column): m_column(column) {}
    void to_dot(MemRef mem, ArrayParent* parent, size_t ndx_in_parent,
                std::ostream& out) override
    {
        m_column.leaf_to_dot(mem, parent, ndx_in_parent, out);
    }
};

void ColumnBaseSimple::tree_to_dot(std::ostream& out) const
{
    ColumnBase::bptree_to_dot(get_root_array(), out);
}

void ColumnBase::bptree_to_dot(const Array* root, std::ostream& out) const
{
    LeafToDot handler(*this);
    root->bptree_to_dot(out, handler);
}

void ColumnBase::dump_node_structure() const
{
    do_dump_node_structure(std::cerr, 0);
}

namespace realm {
namespace _impl {

void leaf_dumper(MemRef mem, Allocator& alloc, std::ostream& out, int level)
{
    Array leaf(alloc);
    leaf.init_from_mem(mem);
    int indent = level * 2;
    out << std::setw(indent) << "" << "Integer leaf (ref: " << leaf.get_ref() << ", "
        "size: " << leaf.size() << ")\n";
    std::ostringstream out_2;
    for (size_t i = 0; i != leaf.size(); ++i) {
        if (i != 0) {
            out_2 << ", ";
            if (out_2.tellp() > 70) {
                out_2 << "...";
                break;
            }
        }
        out_2 << leaf.get(i);
    }
    out << std::setw(indent) << "" << "  Elems: " << out_2.str() << "\n";
}

} // namespace _impl
} // namespace realm

#endif // LCOV_EXCL_STOP ignore debug functions
<|endoftext|>


<commit_before>/*
**  Copyright (C) 2012 Aldebaran Robotics
**  See COPYING for the license
*/

// Disable "'this': used in base member initializer list"
#ifdef _MSC_VER
# pragma warning( push )
# pragma warning(disable: 4355)
#endif

#include "remoteobject_p.hpp"
#include "message.hpp"
#include "transportsocket.hpp"
#include <qi/log.hpp>
#include <boost/thread/mutex.hpp>
#include <boost/dynamic_bitset.hpp>
#include <qi/eventloop.hpp>

qiLogCategory("qimessaging.remoteobject");

namespace qi {

  static qi::MetaObject &createRemoteObjectSpecialMetaObject() {
    static qi::MetaObject *mo = 0;

    if (!mo) {

      mo = new qi::MetaObject;
      qi::MetaObjectBuilder mob;
      mob.addMethod("L", "registerEvent::(IIL)", qi::Message::BoundObjectFunction_RegisterEvent);
      mob.addMethod("v", "unregisterEvent::(IIL)", qi::Message::BoundObjectFunction_UnregisterEvent);
      mob.addMethod("({I(Isss[(ss)]s)}{I(Is)}{I(Iss)}s)", "metaObject::(I)", qi::Message::BoundObjectFunction_MetaObject);

      *mo = mob.metaObject();

      assert(mo->methodId("registerEvent::(IIL)") == qi::Message::BoundObjectFunction_RegisterEvent);
      assert(mo->methodId("unregisterEvent::(IIL)") == qi::Message::BoundObjectFunction_UnregisterEvent);
      assert(mo->methodId("metaObject::(I)") == qi::Message::BoundObjectFunction_MetaObject);
    }
    return *mo;
  }

  RemoteObject::RemoteObject(unsigned int service, qi::TransportSocketPtr socket)
    : ObjectHost(service)
    , _socket()
    , _service(service)
    , _object(1)
    , _linkMessageDispatcher(0)
    , _self(makeDynamicObjectPtr(this, false))
  {
    //simple metaObject with only special methods. (<10)
    setMetaObject(createRemoteObjectSpecialMetaObject());
    setTransportSocket(socket);
    //fetchMetaObject should be called to make sure the metaObject is valid.
  }

  RemoteObject::RemoteObject(unsigned int service, unsigned int object, qi::MetaObject metaObject, TransportSocketPtr socket)
    : ObjectHost(service)
    , _socket()
    , _service(service)
    , _object(object)
    , _linkMessageDispatcher(0)
    , _self(makeDynamicObjectPtr(this, false))
  {
    setMetaObject(metaObject);
    setTransportSocket(socket);
  }

  RemoteObject::~RemoteObject()
  {
    //close may already have been called. (by Session_Service.close)
    close();
  }

  //### RemoteObject

  void RemoteObject::setTransportSocket(qi::TransportSocketPtr socket) {
    if (socket == _socket)
      return;
    if (_socket) {
      _socket->messagePendingDisconnect(_service,
        _object <= Message::GenericObject_Main?TransportSocket::ALL_OBJECTS:_object,
        _linkMessageDispatcher);
      _socket->disconnected.disconnect(_linkDisconnected);
    }
    _socket = socket;
    //do not set the socket on the remote object
    if (socket) {
      _linkMessageDispatcher = _socket->messagePendingConnect(_service,
        _object <= Message::GenericObject_Main?TransportSocket::ALL_OBJECTS:_object,
        boost::bind<void>(&RemoteObject::onMessagePending, this, _1));
      _linkDisconnected      = _socket->disconnected.connect (boost::bind<void>(&RemoteObject::onSocketDisconnected, this, _1));
    }
  }

  //should be done in the object thread
  void RemoteObject::onSocketDisconnected(int error)
  {
    {
      boost::mutex::scoped_lock lock(_mutex);
      std::map<int, qi::Promise<GenericValuePtr> >::iterator it = _promises.begin();
      while (it != _promises.end()) {
        qiLogVerbose() << "Reporting error for request " << it->first << "(socket disconnected)";
        it->second.setError("Socket disconnected");
        _promises.erase(it);
        it = _promises.begin();
      }
    }
  }

  void RemoteObject::onMetaObject(qi::Future<qi::MetaObject> fut, qi::Promise<void> prom) {
    if (fut.hasError()) {
      prom.setError(fut.error());
      return;
    }
    setMetaObject(fut.value());
    prom.setValue(0);
  }

  //retrieve the metaObject from the network
  qi::Future<void> RemoteObject::fetchMetaObject() {
    qi::Promise<void> prom;
    qi::Future<qi::MetaObject> fut = _self->call<qi::MetaObject>("metaObject", 0U);
    fut.connect(boost::bind<void>(&RemoteObject::onMetaObject, this, _1, prom));
    return prom.future();
  }

  //should be done in the object thread
  void RemoteObject::onMessagePending(const qi::Message &msg)
  {
    qiLogDebug() << this << "(" << _service << '/' << _object << " msg " << msg.address() << " " << msg.buffer().size();
    if (msg.object() != _object)
    {
      qiLogDebug() << "Passing message to host";
      ObjectHost::onMessage(msg, _socket);
      return;
    }

    if (msg.type() == qi::Message::Type_Event) {
      SignalBase* sb = signalBase(msg.event());
      if (sb)
      {
        try {
          // Signal associated with properties have incorrect signature,
          // Trust MetaObject.
          //std::string sig = sb->signature();
          const MetaSignal* ms  = _self->metaObject().signal(msg.event());
          std::string sig = ms->signature();
          sig = signatureSplit(sig)[2];

          // Remove top-level tuple
          //sig = sig.substr(1, sig.length()-2);
          GenericValuePtr value = msg.value(sig, _socket);
          GenericFunctionParameters args = value.asTupleValuePtr();
          qiLogDebug() << "Triggering local event listeners";
          sb->trigger(args);
          value.destroy();
        }
        catch (const std::exception& e)
        {
          qiLogWarning() << "Deserialize error on event: " << e.what();
        }
      }
      else
      {
        qiLogWarning() << "Event message on unknown signal " << msg.event();
        qiLogDebug() << metaObject().signalMap().size();
      }
      return;
    }

    if (msg.type() != qi::Message::Type_Reply && msg.type() != qi::Message::Type_Error) {
      qiLogError() << "Message " << msg.address() << " type not handled: " << msg.type();
      return;
    }

    qi::Promise<GenericValuePtr> promise;
    {
      boost::mutex::scoped_lock lock(_mutex);
      std::map<int, qi::Promise<GenericValuePtr> >::iterator it;
      it = _promises.find(msg.id());
      if (it != _promises.end()) {
        promise = _promises[msg.id()];
        _promises.erase(it);
      } else  {
        qiLogError() << "no promise found for req id:" << msg.id()
        << "  obj: " << msg.service() << "  func: " << msg.function();
        return;
      }
    }

    switch (msg.type()) {
      case qi::Message::Type_Reply: {
        // Get call signature
        MetaMethod* mm =  metaObject().method(msg.function());
        if (!mm)
        {
          qiLogError() << "Result for unknown function "
           << msg.function();
           promise.setError("Result for unknown function");
           return;
        }
        try {
          qi::GenericValuePtr val = msg.value(mm->sigreturn(), _socket);
          promise.setValue(val);
        } catch (std::runtime_error &err) {
          promise.setError(err.what());
        }

        qiLogDebug() << "Message passed to promise";
        return;
      }

      case qi::Message::Type_Error: {
        try {
          static std::string sigerr("m");
          qi::GenericValuePtr gvp = msg.value(sigerr, _socket).asDynamic();
          std::string err = gvp.asString();
          qiLogVerbose() << "Received error message"  << msg.address() << ":" << err;
          promise.setError(err);
        } catch (std::runtime_error &e) {
          //houston we have an error about the error..
          promise.setError(e.what());
        }
        return;
      }
      default:
        //not possible
        return;
    }
  }

  qi::Future<GenericValuePtr> RemoteObject::metaCall(Manageable*, unsigned int method, const qi::GenericFunctionParameters &in, MetaCallType callType)
  {
    qi::Promise<GenericValuePtr> out;
    qi::Message msg;
    std::string argsSig("(");
    // apparent signature must match for correct serialization
    for (unsigned i = 0; i < in.size(); ++i)
      argsSig += in[i].signature(false);
    argsSig += ')';
    std::string funcSig =
      signatureSplit(metaObject().method(method)->signature())[2];
    if (funcSig != argsSig)
    {
      std::vector<GenericValuePtr> nargs(in);
      Signature src = Signature(argsSig).begin().children();
      Signature dst = Signature(funcSig).begin().children();
      Signature::iterator its = src.begin(), itd = dst.begin();
      boost::dynamic_bitset<> allocated(nargs.size());
      for (unsigned i = 0; i< nargs.size(); ++i, ++its, ++itd)
      {
        if (*its != *itd)
        {
          Type* target = Type::fromSignature(*itd);
          if (!target)
            throw std::runtime_error("remote call: Failed to obtain a type from signature " + *itd);
          std::pair<GenericValuePtr, bool> c = nargs[i].convert(target);
          if (!c.first.type)
          {
            throw std::runtime_error(
              _QI_LOG_FORMAT("remote call: failed to convert argument %s from %s to %s", i, *its, *itd));
          }
          nargs[i] = c.first;
          allocated[i] = c.second;
        }
      }
      msg.setValues(nargs, this);
      for (unsigned i = 0; i< nargs.size(); ++i)
        if (allocated[i])
        nargs[i].destroy();
    }
    else
      msg.setValues(in, this);

    msg.setType(qi::Message::Type_Call);
    msg.setService(_service);
    msg.setObject(_object);
    msg.setFunction(method);
    // qiLogDebug() << this << " metacall " << msg.service() << " " << msg.function() <<" " << msg.id();
    {
      boost::mutex::scoped_lock lock(_mutex);
      if (_promises.find(msg.id()) != _promises.end())
      {
        qiLogError() << "There is already a pending promise with id "
                                   << msg.id();
      }
      _promises[msg.id()] = out;
    }

    //error will come back as a error message
    if (!_socket || !_socket->isConnected() || !_socket->send(msg)) {
      qi::MetaMethod*   meth = metaObject().method(method);
      std::stringstream ss;
      if (meth) {
        ss << "Network error while sending data to method: '";
        ss << meth->signature();;
        ss << "'.";
      } else {
        ss << "Network error while sending data an unknown method (id=" << method << ").";
      }
      if (!_socket->isConnected()) {
        ss << " Socket is not connected.";
        qiLogVerbose() << ss.str();
      } else {
        qiLogError() << ss.str();
      }
      out.setError(ss.str());

      {
        boost::mutex::scoped_lock lock(_mutex);
        _promises.erase(msg.id());
      }
    }
    return out.future();
  }

  void RemoteObject::metaPost(Manageable*, unsigned int event, const qi::GenericFunctionParameters &args)
  {
    // Bounce the emit request to server
    // TODO: one optimisation that could be done is to trigger the local
    // subscribers immediately.
    // But it is a bit complex, because the server will bounce the
    // event back to us.
    qi::Message msg;
    msg.setValue(qi::makeGenericTuple(args), this);
    msg.setType(Message::Type_Post);
    msg.setService(_service);
    msg.setObject(_object);
    msg.setFunction(event);
    if (!_socket->send(msg)) {
      qiLogError() << "error while emiting event";
    }
  }

  static void onEventConnected(qi::Future<Link> fut, qi::Promise<Link> prom, Link id) {
    if (fut.hasError()) {
      prom.setError(fut.error());
      return;
    }
    prom.setValue(id);
  }

  qi::Future<Link> RemoteObject::metaConnect(unsigned int event, const SignalSubscriber& sub)
  {
    qi::Promise<Link> prom;

    // Bind the subscriber locally.
    Link uid = DynamicObject::metaConnect(event, sub);

    qiLogDebug() <<"connect() to " << event <<" gave " << uid;
    qi::Future<Link> fut = _self->call<Link>("registerEvent", _service, event, uid);
    fut.connect(boost::bind<void>(&onEventConnected, _1, prom, uid));
    return prom.future();
  }

  qi::Future<void> RemoteObject::metaDisconnect(Link linkId)
  {
    unsigned int event = linkId >> 16;
    //disconnect locally
    qi::Future<void> fut = DynamicObject::metaDisconnect(linkId);
    if (fut.hasError())
    {
      std::stringstream ss;
      ss << "Disconnection failure for " << linkId << ", error:" << fut.error();
      qiLogWarning() << ss.str();
      return qi::makeFutureError<void>(ss.str());
    }
    if (_socket->isConnected())
      return _self->call<void>("unregisterEvent", _service, event, linkId);
    return qi::makeFutureError<void>("No remote unregister: socket disconnected");
  }

  void RemoteObject::close() {
    if (_socket) {
      _socket->messagePendingDisconnect(_service,
        _object <= Message::GenericObject_Main?TransportSocket::ALL_OBJECTS:_object,
        _linkMessageDispatcher);
      _socket->disconnected.disconnect(_linkDisconnected);
    }
  }

 qi::Future<GenericValue> RemoteObject::getProperty(unsigned int id)
 {
   qiLogDebug() << "bouncing getProperty";
   // FIXME: perform some validations on this end?
   return _self->call<GenericValue>("getProperty", id);
 }

 qi::Future<void> RemoteObject::setProperty(unsigned int id, GenericValue val)
 {
   qiLogDebug() << "bouncing setProperty";
   return _self->call<void>("setProperty", id, val);
 }
}

#ifdef _MSC_VER
# pragma warning( pop )
#endif
<commit_msg>Remove hard-coded MetaObject signature.<commit_after>/*
**  Copyright (C) 2012 Aldebaran Robotics
**  See COPYING for the license
*/

// Disable "'this': used in base member initializer list"
#ifdef _MSC_VER
# pragma warning( push )
# pragma warning(disable: 4355)
#endif

#include "remoteobject_p.hpp"
#include "message.hpp"
#include "transportsocket.hpp"
#include <qi/log.hpp>
#include <boost/thread/mutex.hpp>
#include <boost/dynamic_bitset.hpp>
#include <qi/eventloop.hpp>

qiLogCategory("qimessaging.remoteobject");

namespace qi {

  static qi::MetaObject &createRemoteObjectSpecialMetaObject() {
    static qi::MetaObject *mo = 0;

    if (!mo) {

      mo = new qi::MetaObject;
      qi::MetaObjectBuilder mob;
      mob.addMethod("L", "registerEvent::(IIL)", qi::Message::BoundObjectFunction_RegisterEvent);
      mob.addMethod("v", "unregisterEvent::(IIL)", qi::Message::BoundObjectFunction_UnregisterEvent);
      mob.addMethod(typeOf<MetaObject>()->signature(), "metaObject::(I)", qi::Message::BoundObjectFunction_MetaObject);

      *mo = mob.metaObject();

      assert(mo->methodId("registerEvent::(IIL)") == qi::Message::BoundObjectFunction_RegisterEvent);
      assert(mo->methodId("unregisterEvent::(IIL)") == qi::Message::BoundObjectFunction_UnregisterEvent);
      assert(mo->methodId("metaObject::(I)") == qi::Message::BoundObjectFunction_MetaObject);
    }
    return *mo;
  }

  RemoteObject::RemoteObject(unsigned int service, qi::TransportSocketPtr socket)
    : ObjectHost(service)
    , _socket()
    , _service(service)
    , _object(1)
    , _linkMessageDispatcher(0)
    , _self(makeDynamicObjectPtr(this, false))
  {
    //simple metaObject with only special methods. (<10)
    setMetaObject(createRemoteObjectSpecialMetaObject());
    setTransportSocket(socket);
    //fetchMetaObject should be called to make sure the metaObject is valid.
  }

  RemoteObject::RemoteObject(unsigned int service, unsigned int object, qi::MetaObject metaObject, TransportSocketPtr socket)
    : ObjectHost(service)
    , _socket()
    , _service(service)
    , _object(object)
    , _linkMessageDispatcher(0)
    , _self(makeDynamicObjectPtr(this, false))
  {
    setMetaObject(metaObject);
    setTransportSocket(socket);
  }

  RemoteObject::~RemoteObject()
  {
    //close may already have been called. (by Session_Service.close)
    close();
  }

  //### RemoteObject

  void RemoteObject::setTransportSocket(qi::TransportSocketPtr socket) {
    if (socket == _socket)
      return;
    if (_socket) {
      _socket->messagePendingDisconnect(_service,
        _object <= Message::GenericObject_Main?TransportSocket::ALL_OBJECTS:_object,
        _linkMessageDispatcher);
      _socket->disconnected.disconnect(_linkDisconnected);
    }
    _socket = socket;
    //do not set the socket on the remote object
    if (socket) {
      _linkMessageDispatcher = _socket->messagePendingConnect(_service,
        _object <= Message::GenericObject_Main?TransportSocket::ALL_OBJECTS:_object,
        boost::bind<void>(&RemoteObject::onMessagePending, this, _1));
      _linkDisconnected      = _socket->disconnected.connect (boost::bind<void>(&RemoteObject::onSocketDisconnected, this, _1));
    }
  }

  //should be done in the object thread
  void RemoteObject::onSocketDisconnected(int error)
  {
    {
      boost::mutex::scoped_lock lock(_mutex);
      std::map<int, qi::Promise<GenericValuePtr> >::iterator it = _promises.begin();
      while (it != _promises.end()) {
        qiLogVerbose() << "Reporting error for request " << it->first << "(socket disconnected)";
        it->second.setError("Socket disconnected");
        _promises.erase(it);
        it = _promises.begin();
      }
    }
  }

  void RemoteObject::onMetaObject(qi::Future<qi::MetaObject> fut, qi::Promise<void> prom) {
    if (fut.hasError()) {
      prom.setError(fut.error());
      return;
    }
    setMetaObject(fut.value());
    prom.setValue(0);
  }

  //retrieve the metaObject from the network
  qi::Future<void> RemoteObject::fetchMetaObject() {
    qi::Promise<void> prom;
    qi::Future<qi::MetaObject> fut = _self->call<qi::MetaObject>("metaObject", 0U);
    fut.connect(boost::bind<void>(&RemoteObject::onMetaObject, this, _1, prom));
    return prom.future();
  }

  //should be done in the object thread
  void RemoteObject::onMessagePending(const qi::Message &msg)
  {
    qiLogDebug() << this << "(" << _service << '/' << _object << " msg " << msg.address() << " " << msg.buffer().size();
    if (msg.object() != _object)
    {
      qiLogDebug() << "Passing message to host";
      ObjectHost::onMessage(msg, _socket);
      return;
    }

    if (msg.type() == qi::Message::Type_Event) {
      SignalBase* sb = signalBase(msg.event());
      if (sb)
      {
        try {
          // Signal associated with properties have incorrect signature,
          // Trust MetaObject.
          //std::string sig = sb->signature();
          const MetaSignal* ms  = _self->metaObject().signal(msg.event());
          std::string sig = ms->signature();
          sig = signatureSplit(sig)[2];

          // Remove top-level tuple
          //sig = sig.substr(1, sig.length()-2);
          GenericValuePtr value = msg.value(sig, _socket);
          GenericFunctionParameters args = value.asTupleValuePtr();
          qiLogDebug() << "Triggering local event listeners";
          sb->trigger(args);
          value.destroy();
        }
        catch (const std::exception& e)
        {
          qiLogWarning() << "Deserialize error on event: " << e.what();
        }
      }
      else
      {
        qiLogWarning() << "Event message on unknown signal " << msg.event();
        qiLogDebug() << metaObject().signalMap().size();
      }
      return;
    }

    if (msg.type() != qi::Message::Type_Reply && msg.type() != qi::Message::Type_Error) {
      qiLogError() << "Message " << msg.address() << " type not handled: " << msg.type();
      return;
    }

    qi::Promise<GenericValuePtr> promise;
    {
      boost::mutex::scoped_lock lock(_mutex);
      std::map<int, qi::Promise<GenericValuePtr> >::iterator it;
      it = _promises.find(msg.id());
      if (it != _promises.end()) {
        promise = _promises[msg.id()];
        _promises.erase(it);
      } else  {
        qiLogError() << "no promise found for req id:" << msg.id()
        << "  obj: " << msg.service() << "  func: " << msg.function();
        return;
      }
    }

    switch (msg.type()) {
      case qi::Message::Type_Reply: {
        // Get call signature
        MetaMethod* mm =  metaObject().method(msg.function());
        if (!mm)
        {
          qiLogError() << "Result for unknown function "
           << msg.function();
           promise.setError("Result for unknown function");
           return;
        }
        try {
          qi::GenericValuePtr val = msg.value(mm->sigreturn(), _socket);
          promise.setValue(val);
        } catch (std::runtime_error &err) {
          promise.setError(err.what());
        }

        qiLogDebug() << "Message passed to promise";
        return;
      }

      case qi::Message::Type_Error: {
        try {
          static std::string sigerr("m");
          qi::GenericValuePtr gvp = msg.value(sigerr, _socket).asDynamic();
          std::string err = gvp.asString();
          qiLogVerbose() << "Received error message"  << msg.address() << ":" << err;
          promise.setError(err);
        } catch (std::runtime_error &e) {
          //houston we have an error about the error..
          promise.setError(e.what());
        }
        return;
      }
      default:
        //not possible
        return;
    }
  }

  qi::Future<GenericValuePtr> RemoteObject::metaCall(Manageable*, unsigned int method, const qi::GenericFunctionParameters &in, MetaCallType callType)
  {
    qi::Promise<GenericValuePtr> out;
    qi::Message msg;
    std::string argsSig("(");
    // apparent signature must match for correct serialization
    for (unsigned i = 0; i < in.size(); ++i)
      argsSig += in[i].signature(false);
    argsSig += ')';
    std::string funcSig =
      signatureSplit(metaObject().method(method)->signature())[2];
    if (funcSig != argsSig)
    {
      std::vector<GenericValuePtr> nargs(in);
      Signature src = Signature(argsSig).begin().children();
      Signature dst = Signature(funcSig).begin().children();
      Signature::iterator its = src.begin(), itd = dst.begin();
      boost::dynamic_bitset<> allocated(nargs.size());
      for (unsigned i = 0; i< nargs.size(); ++i, ++its, ++itd)
      {
        if (*its != *itd)
        {
          Type* target = Type::fromSignature(*itd);
          if (!target)
            throw std::runtime_error("remote call: Failed to obtain a type from signature " + *itd);
          std::pair<GenericValuePtr, bool> c = nargs[i].convert(target);
          if (!c.first.type)
          {
            throw std::runtime_error(
              _QI_LOG_FORMAT("remote call: failed to convert argument %s from %s to %s", i, *its, *itd));
          }
          nargs[i] = c.first;
          allocated[i] = c.second;
        }
      }
      msg.setValues(nargs, this);
      for (unsigned i = 0; i< nargs.size(); ++i)
        if (allocated[i])
        nargs[i].destroy();
    }
    else
      msg.setValues(in, this);

    msg.setType(qi::Message::Type_Call);
    msg.setService(_service);
    msg.setObject(_object);
    msg.setFunction(method);
    // qiLogDebug() << this << " metacall " << msg.service() << " " << msg.function() <<" " << msg.id();
    {
      boost::mutex::scoped_lock lock(_mutex);
      if (_promises.find(msg.id()) != _promises.end())
      {
        qiLogError() << "There is already a pending promise with id "
                                   << msg.id();
      }
      _promises[msg.id()] = out;
    }

    //error will come back as a error message
    if (!_socket || !_socket->isConnected() || !_socket->send(msg)) {
      qi::MetaMethod*   meth = metaObject().method(method);
      std::stringstream ss;
      if (meth) {
        ss << "Network error while sending data to method: '";
        ss << meth->signature();;
        ss << "'.";
      } else {
        ss << "Network error while sending data an unknown method (id=" << method << ").";
      }
      if (!_socket->isConnected()) {
        ss << " Socket is not connected.";
        qiLogVerbose() << ss.str();
      } else {
        qiLogError() << ss.str();
      }
      out.setError(ss.str());

      {
        boost::mutex::scoped_lock lock(_mutex);
        _promises.erase(msg.id());
      }
    }
    return out.future();
  }

  void RemoteObject::metaPost(Manageable*, unsigned int event, const qi::GenericFunctionParameters &args)
  {
    // Bounce the emit request to server
    // TODO: one optimisation that could be done is to trigger the local
    // subscribers immediately.
    // But it is a bit complex, because the server will bounce the
    // event back to us.
    qi::Message msg;
    msg.setValue(qi::makeGenericTuple(args), this);
    msg.setType(Message::Type_Post);
    msg.setService(_service);
    msg.setObject(_object);
    msg.setFunction(event);
    if (!_socket->send(msg)) {
      qiLogError() << "error while emiting event";
    }
  }

  static void onEventConnected(qi::Future<Link> fut, qi::Promise<Link> prom, Link id) {
    if (fut.hasError()) {
      prom.setError(fut.error());
      return;
    }
    prom.setValue(id);
  }

  qi::Future<Link> RemoteObject::metaConnect(unsigned int event, const SignalSubscriber& sub)
  {
    qi::Promise<Link> prom;

    // Bind the subscriber locally.
    Link uid = DynamicObject::metaConnect(event, sub);

    qiLogDebug() <<"connect() to " << event <<" gave " << uid;
    qi::Future<Link> fut = _self->call<Link>("registerEvent", _service, event, uid);
    fut.connect(boost::bind<void>(&onEventConnected, _1, prom, uid));
    return prom.future();
  }

  qi::Future<void> RemoteObject::metaDisconnect(Link linkId)
  {
    unsigned int event = linkId >> 16;
    //disconnect locally
    qi::Future<void> fut = DynamicObject::metaDisconnect(linkId);
    if (fut.hasError())
    {
      std::stringstream ss;
      ss << "Disconnection failure for " << linkId << ", error:" << fut.error();
      qiLogWarning() << ss.str();
      return qi::makeFutureError<void>(ss.str());
    }
    if (_socket->isConnected())
      return _self->call<void>("unregisterEvent", _service, event, linkId);
    return qi::makeFutureError<void>("No remote unregister: socket disconnected");
  }

  void RemoteObject::close() {
    if (_socket) {
      _socket->messagePendingDisconnect(_service,
        _object <= Message::GenericObject_Main?TransportSocket::ALL_OBJECTS:_object,
        _linkMessageDispatcher);
      _socket->disconnected.disconnect(_linkDisconnected);
    }
  }

 qi::Future<GenericValue> RemoteObject::getProperty(unsigned int id)
 {
   qiLogDebug() << "bouncing getProperty";
   // FIXME: perform some validations on this end?
   return _self->call<GenericValue>("getProperty", id);
 }

 qi::Future<void> RemoteObject::setProperty(unsigned int id, GenericValue val)
 {
   qiLogDebug() << "bouncing setProperty";
   return _self->call<void>("setProperty", id, val);
 }
}

#ifdef _MSC_VER
# pragma warning( pop )
#endif
<|endoftext|>


<commit_before>#include "behavior.h"
#include <vector>
#include <string>
#include "input/input-map.h"
#include "command.h"

using namespace std;

namespace Mugen{

Behavior::Behavior(){
}

Behavior::~Behavior(){
}

HumanBehavior::HumanBehavior(InputMap<Mugen::Keys> right, InputMap<Mugen::Keys> left):
right(right),
left(left){
}

InputMap<Keys> & HumanBehavior::getInput(bool facingRight){
    if (facingRight){
        return right;
    }
    return left;
}

vector<string> HumanBehavior::currentCommands(const vector<Command*> & commands, bool reversed){
    vector<string> out;

    InputMap<Mugen::Keys>::Output output = InputManager::getMap(getInput(reversed));

    for (vector<Command*>::const_iterator it = commands.begin(); it != commands.end(); it++){
        Command * command = *it;
        if (command->handle(output)){
            Global::debug(2) << "command: " << command->getName() << endl;
            out.push_back(command->getName());
        }
    }

    return out;

}

HumanBehavior::~HumanBehavior(){
}

AIBehavior::AIBehavior(){
}

vector<string> AIBehavior::currentCommands(const vector<Command*> & commands, bool reversed){
    vector<string> out;
    return out;
}

AIBehavior::~AIBehavior(){
}

}
<commit_msg>make ai do some moves<commit_after>#include "behavior.h"
#include <vector>
#include <string>
#include "input/input-map.h"
#include "command.h"
#include "util/funcs.h"

using namespace std;

namespace Mugen{

namespace PaintownUtil = ::Util;

Behavior::Behavior(){
}

Behavior::~Behavior(){
}

HumanBehavior::HumanBehavior(InputMap<Mugen::Keys> right, InputMap<Mugen::Keys> left):
right(right),
left(left){
}

InputMap<Keys> & HumanBehavior::getInput(bool facingRight){
    if (facingRight){
        return right;
    }
    return left;
}

vector<string> HumanBehavior::currentCommands(const vector<Command*> & commands, bool reversed){
    vector<string> out;

    InputMap<Mugen::Keys>::Output output = InputManager::getMap(getInput(reversed));

    for (vector<Command*>::const_iterator it = commands.begin(); it != commands.end(); it++){
        Command * command = *it;
        if (command->handle(output)){
            Global::debug(2) << "command: " << command->getName() << endl;
            out.push_back(command->getName());
        }
    }

    return out;

}

HumanBehavior::~HumanBehavior(){
}

AIBehavior::AIBehavior(){
}

static string randomCommand(const vector<Command*> & commands){
    if (commands.size() == 0){
        return "";
    }

    int choice = PaintownUtil::rnd(commands.size());
    return commands[choice]->getName();
}

vector<string> AIBehavior::currentCommands(const vector<Command*> & commands, bool reversed){
    vector<string> out;
    if (PaintownUtil::rnd(100) > 90){
        out.push_back(randomCommand(commands));
    }
    return out;
}

AIBehavior::~AIBehavior(){
}

}
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2015 deipi.com LLC and contributors. All rights reserved.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to
 * deal in the Software without restriction, including without limitation the
 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
 * sell copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 * IN THE SOFTWARE.
 */

#include "multivaluerange.h"

#include "length.h"
#include "serialise.h"
#include "stl_serialise.h"

MultipleValueRange::MultipleValueRange(Xapian::valueno slot_, const std::string& start_, const std::string& end_)
	: ValuePostingSource(slot_), start(start_), end(end_), slot(slot_)
{
	set_maxweight(1.0);
}

// Receive start and end did not serialize.
Xapian::Query
MultipleValueRange::getQuery(Xapian::valueno slot_, char field_type, std::string& start_, std::string& end_, const std::string& field_name)
{
	if (start_.empty()) {
		if (end_.empty()){
			return Xapian::Query::MatchAll;
		}
		try {
			end_ = Serialise::serialise(field_type, end_);
		} catch (const Exception& exc) {
			throw MSG_QueryParserError("Failed to serialize: " + field_name + ":" + start_ + ".." + end_ + " like " + Serialise::type(field_type) + " (" + exc.what() +")");
		}
		MultipleValueLE mvle(slot_, end_);
		return Xapian::Query(&mvle);
	} else if (end_.empty()) {
		try {
			start_ = Serialise::serialise(field_type, start_);
		} catch (const Exception& exc) {
			throw MSG_QueryParserError("Failed to serialize: " + field_name + ":" + start_ + ".." + end_ + " like " + Serialise::type(field_type) + " (" + exc.what() +")");
		}
		MultipleValueGE mvge(slot_, start_);
		return Xapian::Query(&mvge);
	}

	// Multiple Value Range
	try {
		start_ = Serialise::serialise(field_type, start_);
		end_ = Serialise::serialise(field_type, end_);
	} catch (const Exception& exc) {
			throw MSG_QueryParserError("Failed to serialize: " + field_name + ":" + start_ + ".." + end_ + " like " + Serialise::type(field_type) + " (" + exc.what() +")");
	}
	if (start_ > end_) return Xapian::Query::MatchNothing;
	MultipleValueRange mvr(slot_, start_, end_);
	return Xapian::Query(&mvr);
}

bool
MultipleValueRange::insideRange()  const noexcept
{
	StringList list;
	list.unserialise(get_value());
	for (const auto& value_ : list) {
		if (value_ >= start && value_ <= end) {
			return true;
		}
	}
	return false;
}

void
MultipleValueRange::next(double min_wt)
{
	Xapian::ValuePostingSource::next(min_wt);
	while (!at_end()) {
		if (insideRange()) break;
		Xapian::ValuePostingSource::next(min_wt);
	}
}

void
MultipleValueRange::skip_to(Xapian::docid min_docid, double min_wt)
{
	Xapian::ValuePostingSource::skip_to(min_docid, min_wt);
	while (!at_end()) {
		if (insideRange()) break;
		Xapian::ValuePostingSource::next(min_wt);
	}
}

bool
MultipleValueRange::check(Xapian::docid min_docid, double min_wt)
{
	if (!ValuePostingSource::check(min_docid, min_wt)) {
		// check returned false, so we know the document is not in the source.
		return false;
	}

	if (at_end()) {
		// return true, since we're definitely at the end of the list.
		return true;
	}

	return insideRange();
}

double
MultipleValueRange::get_weight() const
{
	return 1.0;
}

MultipleValueRange*
MultipleValueRange::clone() const
{
	return new MultipleValueRange(slot, start, end);
}

std::string
MultipleValueRange::name() const
{
	return "MultipleValueRange";
}

std::string
MultipleValueRange::serialise() const
{
	std::string serialised, values, s_slot(Xapian::sortable_serialise(slot));
	values.append(serialise_length(s_slot.size()));
	values.append(s_slot);
	values.append(serialise_length(start.size()));
	values.append(start);
	values.append(serialise_length(end.size()));
	values.append(end);
	serialised.append(serialise_length(values.size()));
	serialised.append(values);
	return serialised;
}

MultipleValueRange*
MultipleValueRange::unserialise_with_registry(const std::string& s, const Xapian::Registry&) const
{
	StringList data;
	data.unserialise(s);
	return new MultipleValueRange(Xapian::sortable_unserialise(data[0]), data[1], data[2]);
}

void
MultipleValueRange::init(const Xapian::Database& db_)
{
	Xapian::ValuePostingSource::init(db_);

	// Possible that no documents are in range.
	set_termfreq_min(0);
}

std::string
MultipleValueRange::get_description() const
{
	std::string result("MultipleValueRange ");
	result += std::to_string(slot) + " " + start;
	result += " " + end;
	return result;
}

MultipleValueGE::MultipleValueGE(Xapian::valueno slot_, const std::string& start_)
	: ValuePostingSource(slot_), start(start_), slot(slot_)
{
	set_maxweight(1.0);
}

bool
MultipleValueGE::insideRange() const noexcept
{
	StringList list;
	list.unserialise(get_value());
	for (const auto& value_ : list) {
		if (value_ >= start) {
			return true;
		}
	}
	return false;
}

void
MultipleValueGE::next(double min_wt)
{
	Xapian::ValuePostingSource::next(min_wt);
	while (!at_end()) {
		if (insideRange()) break;
		Xapian::ValuePostingSource::next(min_wt);
	}
}

void
MultipleValueGE::skip_to(Xapian::docid min_docid, double min_wt)
{
	Xapian::ValuePostingSource::skip_to(min_docid, min_wt);
	while (!at_end()) {
		if (insideRange()) break;
		Xapian::ValuePostingSource::next(min_wt);
	}
}

bool
MultipleValueGE::check(Xapian::docid min_docid, double min_wt)
{
	if (!ValuePostingSource::check(min_docid, min_wt)) {
		// check returned false, so we know the document is not in the source.
		return false;
	}

	if (at_end()) {
		// return true, since we're definitely at the end of the list.
		return true;
	}

	return insideRange();
}

double
MultipleValueGE::get_weight() const
{
	return 1.0;
}

MultipleValueGE*
MultipleValueGE::clone() const
{
	return new MultipleValueGE(slot, start);
}

std::string
MultipleValueGE::name() const
{
	return "MultipleValueGE";
}

std::string
MultipleValueGE::serialise() const
{
	std::string serialised, values, s_slot(Xapian::sortable_serialise(slot));
	values.append(serialise_length(s_slot.size()));
	values.append(s_slot);
	values.append(serialise_length(start.size()));
	values.append(start);
	serialised.append(serialise_length(values.size()));
	serialised.append(values);
	return serialised;
}

MultipleValueGE*
MultipleValueGE::unserialise_with_registry(const std::string& s, const Xapian::Registry&) const
{
	StringList data;
	data.unserialise(s);
	return new MultipleValueGE(Xapian::sortable_unserialise(data[0]), data[1]);
}

void
MultipleValueGE::init(const Xapian::Database& db_)
{
	Xapian::ValuePostingSource::init(db_);

	// Possible that no documents are in range.
	set_termfreq_min(0);
}

std::string
MultipleValueGE::get_description() const
{
	std::string result("MultipleValueGE ");
	result += std::to_string(slot) + " " + start + ")";
	return result;
}

MultipleValueLE::MultipleValueLE(Xapian::valueno slot_, const std::string& end_)
	: ValuePostingSource(slot_), end(end_), slot(slot_)
{
	set_maxweight(1.0);
}

bool
MultipleValueLE::insideRange() const noexcept
{
	StringList list;
	list.unserialise(get_value());
	for (const auto& value_ : list) {
		if (value_ <= end) {
			return true;
		}
	}
	return false;
}

void
MultipleValueLE::next(double min_wt)
{
	Xapian::ValuePostingSource::next(min_wt);
	while (!at_end()) {
		if (insideRange()) break;
		Xapian::ValuePostingSource::next(min_wt);
	}
}

void
MultipleValueLE::skip_to(Xapian::docid min_docid, double min_wt)
{
	Xapian::ValuePostingSource::skip_to(min_docid, min_wt);
	while (!at_end()) {
		if (insideRange()) break;
		Xapian::ValuePostingSource::next(min_wt);
	}
}

bool
MultipleValueLE::check(Xapian::docid min_docid, double min_wt)
{
	if (!ValuePostingSource::check(min_docid, min_wt)) {
		// check returned false, so we know the document is not in the source.
		return false;
	}

	if (at_end()) {
		// return true, since we're definitely at the end of the list.
		return true;
	}

	return insideRange();
}

double
MultipleValueLE::get_weight() const
{
	return 1.0;
}

MultipleValueLE*
MultipleValueLE::clone() const
{
	return new MultipleValueLE(slot, end);
}

std::string
MultipleValueLE::name() const
{
	return "MultipleValueLE";
}

std::string
MultipleValueLE::serialise() const
{
	std::string serialised, values, s_slot(Xapian::sortable_serialise(slot));
	values.append(serialise_length(s_slot.size()));
	values.append(s_slot);
	values.append(serialise_length(end.size()));
	values.append(end);
	serialised.append(serialise_length(values.size()));
	serialised.append(values);
	return serialised;
}

MultipleValueLE*
MultipleValueLE::unserialise_with_registry(const std::string& s, const Xapian::Registry&) const
{
	StringList data;
	data.unserialise(s);
	return new MultipleValueLE(Xapian::sortable_unserialise(data[0]), data[1]);
}

void
MultipleValueLE::init(const Xapian::Database& db_)
{
	Xapian::ValuePostingSource::init(db_);

	// Possible that no documents are in range.
	set_termfreq_min(0);
}

std::string
MultipleValueLE::get_description() const
{
	std::string result("MultipleValueLE ");
	result += std::to_string(slot) + " " + end + ")";
	return result;
}
<commit_msg>Using get_slot from xapian instead attribute class<commit_after>/*
 * Copyright (C) 2015 deipi.com LLC and contributors. All rights reserved.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to
 * deal in the Software without restriction, including without limitation the
 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
 * sell copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 * IN THE SOFTWARE.
 */

#include "multivaluerange.h"

#include "length.h"
#include "serialise.h"
#include "stl_serialise.h"

MultipleValueRange::MultipleValueRange(Xapian::valueno slot_, const std::string& start_, const std::string& end_)
	: ValuePostingSource(slot_), start(start_), end(end_), slot(slot_)
{
	set_maxweight(1.0);
}

// Receive start and end did not serialize.
Xapian::Query
MultipleValueRange::getQuery(Xapian::valueno slot_, char field_type, std::string& start_, std::string& end_, const std::string& field_name)
{
	if (start_.empty()) {
		if (end_.empty()){
			return Xapian::Query::MatchAll;
		}
		try {
			end_ = Serialise::serialise(field_type, end_);
		} catch (const Exception& exc) {
			throw MSG_QueryParserError("Failed to serialize: " + field_name + ":" + start_ + ".." + end_ + " like " + Serialise::type(field_type) + " (" + exc.what() +")");
		}
		MultipleValueLE mvle(slot_, end_);
		return Xapian::Query(&mvle);
	} else if (end_.empty()) {
		try {
			start_ = Serialise::serialise(field_type, start_);
		} catch (const Exception& exc) {
			throw MSG_QueryParserError("Failed to serialize: " + field_name + ":" + start_ + ".." + end_ + " like " + Serialise::type(field_type) + " (" + exc.what() +")");
		}
		MultipleValueGE mvge(slot_, start_);
		return Xapian::Query(&mvge);
	}

	// Multiple Value Range
	try {
		start_ = Serialise::serialise(field_type, start_);
		end_ = Serialise::serialise(field_type, end_);
	} catch (const Exception& exc) {
			throw MSG_QueryParserError("Failed to serialize: " + field_name + ":" + start_ + ".." + end_ + " like " + Serialise::type(field_type) + " (" + exc.what() +")");
	}
	if (start_ > end_) return Xapian::Query::MatchNothing;
	MultipleValueRange mvr(slot_, start_, end_);
	return Xapian::Query(&mvr);
}

bool
MultipleValueRange::insideRange()  const noexcept
{
	StringList list;
	list.unserialise(get_value());
	for (const auto& value_ : list) {
		if (value_ >= start && value_ <= end) {
			return true;
		}
	}
	return false;
}

void
MultipleValueRange::next(double min_wt)
{
	Xapian::ValuePostingSource::next(min_wt);
	while (!at_end()) {
		if (insideRange()) break;
		Xapian::ValuePostingSource::next(min_wt);
	}
}

void
MultipleValueRange::skip_to(Xapian::docid min_docid, double min_wt)
{
	Xapian::ValuePostingSource::skip_to(min_docid, min_wt);
	while (!at_end()) {
		if (insideRange()) break;
		Xapian::ValuePostingSource::next(min_wt);
	}
}

bool
MultipleValueRange::check(Xapian::docid min_docid, double min_wt)
{
	if (!ValuePostingSource::check(min_docid, min_wt)) {
		// check returned false, so we know the document is not in the source.
		return false;
	}

	if (at_end()) {
		// return true, since we're definitely at the end of the list.
		return true;
	}

	return insideRange();
}

double
MultipleValueRange::get_weight() const
{
	return 1.0;
}

MultipleValueRange*
MultipleValueRange::clone() const
{
	return new MultipleValueRange(get_slot(), start, end);
}

std::string
MultipleValueRange::name() const
{
	return "MultipleValueRange";
}

std::string
MultipleValueRange::serialise() const
{
	std::string serialised, values, s_slot(Xapian::sortable_serialise(get_slot()));
	values.append(serialise_length(s_slot.size()));
	values.append(s_slot);
	values.append(serialise_length(start.size()));
	values.append(start);
	values.append(serialise_length(end.size()));
	values.append(end);
	serialised.append(serialise_length(values.size()));
	serialised.append(values);
	return serialised;
}

MultipleValueRange*
MultipleValueRange::unserialise_with_registry(const std::string& s, const Xapian::Registry&) const
{
	StringList data;
	data.unserialise(s);
	return new MultipleValueRange(Xapian::sortable_unserialise(data[0]), data[1], data[2]);
}

void
MultipleValueRange::init(const Xapian::Database& db_)
{
	Xapian::ValuePostingSource::init(db_);

	// Possible that no documents are in range.
	set_termfreq_min(0);
}

std::string
MultipleValueRange::get_description() const
{
	std::string result("MultipleValueRange ");
	result += std::to_string(get_slot()) + " " + start;
	result += " " + end;
	return result;
}

MultipleValueGE::MultipleValueGE(Xapian::valueno slot_, const std::string& start_)
	: ValuePostingSource(slot_), start(start_), slot(slot_)
{
	set_maxweight(1.0);
}

bool
MultipleValueGE::insideRange() const noexcept
{
	StringList list;
	list.unserialise(get_value());
	for (const auto& value_ : list) {
		if (value_ >= start) {
			return true;
		}
	}
	return false;
}

void
MultipleValueGE::next(double min_wt)
{
	Xapian::ValuePostingSource::next(min_wt);
	while (!at_end()) {
		if (insideRange()) break;
		Xapian::ValuePostingSource::next(min_wt);
	}
}

void
MultipleValueGE::skip_to(Xapian::docid min_docid, double min_wt)
{
	Xapian::ValuePostingSource::skip_to(min_docid, min_wt);
	while (!at_end()) {
		if (insideRange()) break;
		Xapian::ValuePostingSource::next(min_wt);
	}
}

bool
MultipleValueGE::check(Xapian::docid min_docid, double min_wt)
{
	if (!ValuePostingSource::check(min_docid, min_wt)) {
		// check returned false, so we know the document is not in the source.
		return false;
	}

	if (at_end()) {
		// return true, since we're definitely at the end of the list.
		return true;
	}

	return insideRange();
}

double
MultipleValueGE::get_weight() const
{
	return 1.0;
}

MultipleValueGE*
MultipleValueGE::clone() const
{
	return new MultipleValueGE(get_slot(), start);
}

std::string
MultipleValueGE::name() const
{
	return "MultipleValueGE";
}

std::string
MultipleValueGE::serialise() const
{
	std::string serialised, values, s_slot(Xapian::sortable_serialise(get_slot()));
	values.append(serialise_length(s_slot.size()));
	values.append(s_slot);
	values.append(serialise_length(start.size()));
	values.append(start);
	serialised.append(serialise_length(values.size()));
	serialised.append(values);
	return serialised;
}

MultipleValueGE*
MultipleValueGE::unserialise_with_registry(const std::string& s, const Xapian::Registry&) const
{
	StringList data;
	data.unserialise(s);
	return new MultipleValueGE(Xapian::sortable_unserialise(data[0]), data[1]);
}

void
MultipleValueGE::init(const Xapian::Database& db_)
{
	Xapian::ValuePostingSource::init(db_);

	// Possible that no documents are in range.
	set_termfreq_min(0);
}

std::string
MultipleValueGE::get_description() const
{
	std::string result("MultipleValueGE ");
	result += std::to_string(get_slot()) + " " + start + ")";
	return result;
}

MultipleValueLE::MultipleValueLE(Xapian::valueno slot_, const std::string& end_)
	: ValuePostingSource(slot_), end(end_), slot(slot_)
{
	set_maxweight(1.0);
}

bool
MultipleValueLE::insideRange() const noexcept
{
	StringList list;
	list.unserialise(get_value());
	for (const auto& value_ : list) {
		if (value_ <= end) {
			return true;
		}
	}
	return false;
}

void
MultipleValueLE::next(double min_wt)
{
	Xapian::ValuePostingSource::next(min_wt);
	while (!at_end()) {
		if (insideRange()) break;
		Xapian::ValuePostingSource::next(min_wt);
	}
}

void
MultipleValueLE::skip_to(Xapian::docid min_docid, double min_wt)
{
	Xapian::ValuePostingSource::skip_to(min_docid, min_wt);
	while (!at_end()) {
		if (insideRange()) break;
		Xapian::ValuePostingSource::next(min_wt);
	}
}

bool
MultipleValueLE::check(Xapian::docid min_docid, double min_wt)
{
	if (!ValuePostingSource::check(min_docid, min_wt)) {
		// check returned false, so we know the document is not in the source.
		return false;
	}

	if (at_end()) {
		// return true, since we're definitely at the end of the list.
		return true;
	}

	return insideRange();
}

double
MultipleValueLE::get_weight() const
{
	return 1.0;
}

MultipleValueLE*
MultipleValueLE::clone() const
{
	return new MultipleValueLE(get_slot(), end);
}

std::string
MultipleValueLE::name() const
{
	return "MultipleValueLE";
}

std::string
MultipleValueLE::serialise() const
{
	std::string serialised, values, s_slot(Xapian::sortable_serialise(get_slot()));
	values.append(serialise_length(s_slot.size()));
	values.append(s_slot);
	values.append(serialise_length(end.size()));
	values.append(end);
	serialised.append(serialise_length(values.size()));
	serialised.append(values);
	return serialised;
}

MultipleValueLE*
MultipleValueLE::unserialise_with_registry(const std::string& s, const Xapian::Registry&) const
{
	StringList data;
	data.unserialise(s);
	return new MultipleValueLE(Xapian::sortable_unserialise(data[0]), data[1]);
}

void
MultipleValueLE::init(const Xapian::Database& db_)
{
	Xapian::ValuePostingSource::init(db_);

	// Possible that no documents are in range.
	set_termfreq_min(0);
}

std::string
MultipleValueLE::get_description() const
{
	std::string result("MultipleValueLE ");
	result += std::to_string(get_slot()) + " " + end + ")";
	return result;
}
<|endoftext|>


<commit_before>// This file is part of the "x0" project, http://xzero.io/
//   (c) 2009-2014 Christian Parpart <trapni@gmail.com>
//
// Licensed under the MIT License (the "License"); you may not use this
// file except in compliance with the License. You may obtain a copy of
// the License at: http://opensource.org/licenses/MIT

#include <xzero/net/Connection.h>
#include <xzero/net/EndPoint.h>
#include <algorithm>

namespace xzero {

Connection::Connection(std::shared_ptr<EndPoint> endpoint,
                       Executor* executor)
    : endpoint_(endpoint),
      executor_(executor),
      listeners_() {

  if (endpoint_) {
    endpoint_->setConnection(this);
  }
}

Connection::~Connection() {
}

void Connection::onOpen() {
  for (ConnectionListener* listener : listeners_) {
    listener->onOpened(this);
  }
}

void Connection::onClose() {
  for (ConnectionListener* listener : listeners_) {
    listener->onClosed(this);
  }
}

void Connection::addListener(ConnectionListener* listener) {
  listeners_.push_back(listener);
}

void Connection::close() {
  if (endpoint_) {
    endpoint_->close();
  }
}

void Connection::setInputBufferSize(size_t size) {
  // default no-op
}

void Connection::wantFill() {
  // register read-event
  endpoint()->wantFill();
}

void Connection::wantFlush(bool enable) {
  // register write-event
  endpoint()->wantFlush(enable);
}

void Connection::onInterestFailure(const std::exception& error) {
  abort();
}

bool Connection::onReadTimeout() {
  // inform caller to close the endpoint
  return true;
}

// {{{ ConnectionListener impl
ConnectionListener::~ConnectionListener() {
}

void ConnectionListener::onOpened(Connection* connection) {
}

void ConnectionListener::onClosed(Connection* connection) {
}
// }}}

}  // namespace xzero
<commit_msg>Connection::onInterestFailure: invoke right default call<commit_after>// This file is part of the "x0" project, http://xzero.io/
//   (c) 2009-2014 Christian Parpart <trapni@gmail.com>
//
// Licensed under the MIT License (the "License"); you may not use this
// file except in compliance with the License. You may obtain a copy of
// the License at: http://opensource.org/licenses/MIT

#include <xzero/net/Connection.h>
#include <xzero/net/EndPoint.h>
#include <algorithm>

namespace xzero {

Connection::Connection(std::shared_ptr<EndPoint> endpoint,
                       Executor* executor)
    : endpoint_(endpoint),
      executor_(executor),
      listeners_() {

  if (endpoint_) {
    endpoint_->setConnection(this);
  }
}

Connection::~Connection() {
}

void Connection::onOpen() {
  for (ConnectionListener* listener : listeners_) {
    listener->onOpened(this);
  }
}

void Connection::onClose() {
  for (ConnectionListener* listener : listeners_) {
    listener->onClosed(this);
  }
}

void Connection::addListener(ConnectionListener* listener) {
  listeners_.push_back(listener);
}

void Connection::close() {
  if (endpoint_) {
    endpoint_->close();
  }
}

void Connection::setInputBufferSize(size_t size) {
  // default no-op
}

void Connection::wantFill() {
  // register read-event
  endpoint()->wantFill();
}

void Connection::wantFlush(bool enable) {
  // register write-event
  endpoint()->wantFlush(enable);
}

void Connection::onInterestFailure(const std::exception& error) {
  close();
}

bool Connection::onReadTimeout() {
  // inform caller to close the endpoint
  return true;
}

// {{{ ConnectionListener impl
ConnectionListener::~ConnectionListener() {
}

void ConnectionListener::onOpened(Connection* connection) {
}

void ConnectionListener::onClosed(Connection* connection) {
}
// }}}

}  // namespace xzero
<|endoftext|>


<commit_before>// Filename: string_utils.cxx
// Created by:  drose (18Jan99)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) 2001, Disney Enterprises, Inc.  All rights reserved
//
// All use of this software is subject to the terms of the Panda 3d
// Software license.  You should have received a copy of this license
// along with this source code; you will also find a current copy of
// the license at http://www.panda3d.org/license.txt .
//
// To contact the maintainers of this program write to
// panda3d@yahoogroups.com .
//
////////////////////////////////////////////////////////////////////

#include "string_utils.h"

#include <ctype.h>

// Case-insensitive string comparison, from Stroustrup's C++ third edition.
// Works like strcmp().
int
cmp_nocase(const string &s, const string &s2) {
  string::const_iterator p = s.begin();
  string::const_iterator p2 = s2.begin();

  while (p != s.end() && p2 != s2.end()) {
    if (toupper(*p) != toupper(*p2)) {
      return (toupper(*p) < toupper(*p2)) ? -1 : 1;
    }
    ++p;
    ++p2;
  }

  return (s2.size() == s.size()) ? 0 :
    (s.size() < s2.size()) ? -1 : 1;  // size is unsigned
}

INLINE int
toupper_uh(int ch) {
  return (ch == '_') ? '-' : toupper(ch);
}

int
cmp_nocase_uh(const string &s, const string &s2) {
  string::const_iterator p = s.begin();
  string::const_iterator p2 = s2.begin();

  while (p != s.end() && p2 != s2.end()) {
    if (toupper_uh(*p) != toupper_uh(*p2)) {
      return (toupper_uh(*p) < toupper_uh(*p2)) ? -1 : 1;
    }
    ++p;
    ++p2;
  }

  return (s2.size() == s.size()) ? 0 :
    (s.size() < s2.size()) ? -1 : 1;  // size is unsigned
}

////////////////////////////////////////////////////////////////////
//     Function: downcase
//  Description: Returns the input string with all uppercase letters
//               converted to lowercase.
////////////////////////////////////////////////////////////////////
string
downcase(const string &s) {
  string result;
  string::const_iterator p;
  for (p = s.begin(); p != s.end(); ++p) {
    result += tolower(*p);
  }
  return result;
}

////////////////////////////////////////////////////////////////////
//     Function: upcase
//  Description: Returns the input string with all lowercase letters
//               converted to uppercase.
////////////////////////////////////////////////////////////////////
string
upcase(const string &s) {
  string result;
  string::const_iterator p;
  for (p = s.begin(); p != s.end(); ++p) {
    result += toupper(*p);
  }
  return result;
}

////////////////////////////////////////////////////////////////////
//     Function: extract_words
//  Description: Divides the string into a number of words according
//               to whitespace.  The words vector should be cleared by
//               the user before calling; otherwise, the list of words
//               in the string will be appended to the end of whatever
//               was there before.
//
//               The return value is the number of words extracted.
////////////////////////////////////////////////////////////////////
int
extract_words(const string &str, vector_string &words) {
  int num_words = 0;

  size_t pos = 0;
  while (pos < str.length() && isspace((unsigned int)str[pos])) {
    pos++;
  }
  while (pos < str.length()) {
    size_t word_start = pos;
    while (pos < str.length() && !isspace((unsigned int)str[pos])) {
      pos++;
    }
    words.push_back(str.substr(word_start, pos - word_start));
    num_words++;

    while (pos < str.length() && isspace((unsigned int)str[pos])) {
      pos++;
    }
  }

  return num_words;
}

////////////////////////////////////////////////////////////////////
//     Function: tokenize
//  Description: Chops the source string up into pieces delimited by
//               any of the characters specified in delimiters.
//               Repeated delimiter characters represent zero-length
//               tokens.
//
//               It is the user's responsibility to ensure the output
//               vector is cleared before calling this function; the
//               results will simply be appended to the end of the
//               vector.
////////////////////////////////////////////////////////////////////
void
tokenize(const string &str, vector_string &words, const string &delimiters) {
  size_t p = 0;
  while (p < str.length()) {
    size_t q = str.find_first_of(delimiters, p);
    if (q == string::npos) {
      words.push_back(str.substr(p));
      return;
    }
    words.push_back(str.substr(p, q - p));
    p = q + 1;
  }
  words.push_back(string());
}

////////////////////////////////////////////////////////////////////
//     Function: trim_left
//  Description: Returns a new string representing the contents of the
//               given string with the leading whitespace removed.
////////////////////////////////////////////////////////////////////
string
trim_left(const string &str) {
  size_t begin = 0;
  while (begin < str.size() && isspace((unsigned int)str[begin])) {
    begin++;
  }

  return str.substr(begin);
}

////////////////////////////////////////////////////////////////////
//     Function: trim_right
//  Description: Returns a new string representing the contents of the
//               given string with the trailing whitespace removed.
////////////////////////////////////////////////////////////////////
string
trim_right(const string &str) {
  size_t begin = 0;
  size_t end = str.size();
  while (end > begin && isspace((unsigned int)str[end - 1])) {
    end--;
  }

  return str.substr(begin, end - begin);
}

////////////////////////////////////////////////////////////////////
//     Function: string_to_int
//  Description: A string-interface wrapper around the C library
//               strtol().  This parses the ASCII representation of an
//               integer, and then sets tail to everything that
//               follows the first valid integer read.  If, on exit,
//               str == tail, there was no valid integer in the
//               source string; if !tail.empty(), there was garbage
//               after the integer.
//
//               It is legal if str and tail refer to the same string.
////////////////////////////////////////////////////////////////////
int
string_to_int(const string &str, string &tail) {
  const char *nptr = str.c_str();
  char *endptr;
  int result = strtol(nptr, &endptr, 10);
  tail = endptr;
  return result;
}

////////////////////////////////////////////////////////////////////
//     Function: string_to_int
//  Description: Another flavor of string_to_int(), this one returns
//               true if the string is a perfectly valid integer (and
//               sets result to that value), or false otherwise.
////////////////////////////////////////////////////////////////////
bool
string_to_int(const string &str, int &result) {
  string tail;
  result = string_to_int(str, tail);
  return tail.empty();
}

////////////////////////////////////////////////////////////////////
//     Function: string_to_double
//  Description: A string-interface wrapper around the C library
//               strtol().  This parses the ASCII representation of an
//               floating-point number, and then sets tail to
//               everything that follows the first valid integer read.
//               If, on exit, str == tail, there was no valid integer
//               in the source string; if !tail.empty(), there was
//               garbage after the number.
//
//               It is legal if str and tail refer to the same string.
////////////////////////////////////////////////////////////////////
double
string_to_double(const string &str, string &tail) {
  const char *nptr = str.c_str();
  char *endptr;
  double result = strtod(nptr, &endptr);
  tail = endptr;
  return result;
}

////////////////////////////////////////////////////////////////////
//     Function: string_to_double
//  Description: Another flavor of string_to_double(), this one
//               returns true if the string is a perfectly valid
//               number (and sets result to that value), or false
//               otherwise.
////////////////////////////////////////////////////////////////////
bool
string_to_double(const string &str, double &result) {
  string tail;
  result = string_to_double(str, tail);
  return tail.empty();
}

////////////////////////////////////////////////////////////////////
//     Function: string_to_float
//  Description: Another flavor of string_to_float(), this one
//               returns true if the string is a perfectly valid
//               number (and sets result to that value), or false
//               otherwise.
////////////////////////////////////////////////////////////////////
bool
string_to_float(const string &str, float &result) {
  string tail;
  result = (float)string_to_double(str, tail);
  return tail.empty();
}
<commit_msg>reserve<commit_after>// Filename: string_utils.cxx
// Created by:  drose (18Jan99)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) 2001, Disney Enterprises, Inc.  All rights reserved
//
// All use of this software is subject to the terms of the Panda 3d
// Software license.  You should have received a copy of this license
// along with this source code; you will also find a current copy of
// the license at http://www.panda3d.org/license.txt .
//
// To contact the maintainers of this program write to
// panda3d@yahoogroups.com .
//
////////////////////////////////////////////////////////////////////

#include "string_utils.h"

#include <ctype.h>

// Case-insensitive string comparison, from Stroustrup's C++ third edition.
// Works like strcmp().
int
cmp_nocase(const string &s, const string &s2) {
  string::const_iterator p = s.begin();
  string::const_iterator p2 = s2.begin();

  while (p != s.end() && p2 != s2.end()) {
    if (toupper(*p) != toupper(*p2)) {
      return (toupper(*p) < toupper(*p2)) ? -1 : 1;
    }
    ++p;
    ++p2;
  }

  return (s2.size() == s.size()) ? 0 :
    (s.size() < s2.size()) ? -1 : 1;  // size is unsigned
}

INLINE int
toupper_uh(int ch) {
  return (ch == '_') ? '-' : toupper(ch);
}

int
cmp_nocase_uh(const string &s, const string &s2) {
  string::const_iterator p = s.begin();
  string::const_iterator p2 = s2.begin();

  while (p != s.end() && p2 != s2.end()) {
    if (toupper_uh(*p) != toupper_uh(*p2)) {
      return (toupper_uh(*p) < toupper_uh(*p2)) ? -1 : 1;
    }
    ++p;
    ++p2;
  }

  return (s2.size() == s.size()) ? 0 :
    (s.size() < s2.size()) ? -1 : 1;  // size is unsigned
}

////////////////////////////////////////////////////////////////////
//     Function: downcase
//  Description: Returns the input string with all uppercase letters
//               converted to lowercase.
////////////////////////////////////////////////////////////////////
string
downcase(const string &s) {
  string result;
  result.reserve(s.size());
  string::const_iterator p;
  for (p = s.begin(); p != s.end(); ++p) {
    result += tolower(*p);
  }
  return result;
}

////////////////////////////////////////////////////////////////////
//     Function: upcase
//  Description: Returns the input string with all lowercase letters
//               converted to uppercase.
////////////////////////////////////////////////////////////////////
string
upcase(const string &s) {
  string result;
  result.reserve(s.size());
  string::const_iterator p;
  for (p = s.begin(); p != s.end(); ++p) {
    result += toupper(*p);
  }
  return result;
}

////////////////////////////////////////////////////////////////////
//     Function: extract_words
//  Description: Divides the string into a number of words according
//               to whitespace.  The words vector should be cleared by
//               the user before calling; otherwise, the list of words
//               in the string will be appended to the end of whatever
//               was there before.
//
//               The return value is the number of words extracted.
////////////////////////////////////////////////////////////////////
int
extract_words(const string &str, vector_string &words) {
  int num_words = 0;

  size_t pos = 0;
  while (pos < str.length() && isspace((unsigned int)str[pos])) {
    pos++;
  }
  while (pos < str.length()) {
    size_t word_start = pos;
    while (pos < str.length() && !isspace((unsigned int)str[pos])) {
      pos++;
    }
    words.push_back(str.substr(word_start, pos - word_start));
    num_words++;

    while (pos < str.length() && isspace((unsigned int)str[pos])) {
      pos++;
    }
  }

  return num_words;
}

////////////////////////////////////////////////////////////////////
//     Function: tokenize
//  Description: Chops the source string up into pieces delimited by
//               any of the characters specified in delimiters.
//               Repeated delimiter characters represent zero-length
//               tokens.
//
//               It is the user's responsibility to ensure the output
//               vector is cleared before calling this function; the
//               results will simply be appended to the end of the
//               vector.
////////////////////////////////////////////////////////////////////
void
tokenize(const string &str, vector_string &words, const string &delimiters) {
  size_t p = 0;
  while (p < str.length()) {
    size_t q = str.find_first_of(delimiters, p);
    if (q == string::npos) {
      words.push_back(str.substr(p));
      return;
    }
    words.push_back(str.substr(p, q - p));
    p = q + 1;
  }
  words.push_back(string());
}

////////////////////////////////////////////////////////////////////
//     Function: trim_left
//  Description: Returns a new string representing the contents of the
//               given string with the leading whitespace removed.
////////////////////////////////////////////////////////////////////
string
trim_left(const string &str) {
  size_t begin = 0;
  while (begin < str.size() && isspace((unsigned int)str[begin])) {
    begin++;
  }

  return str.substr(begin);
}

////////////////////////////////////////////////////////////////////
//     Function: trim_right
//  Description: Returns a new string representing the contents of the
//               given string with the trailing whitespace removed.
////////////////////////////////////////////////////////////////////
string
trim_right(const string &str) {
  size_t begin = 0;
  size_t end = str.size();
  while (end > begin && isspace((unsigned int)str[end - 1])) {
    end--;
  }

  return str.substr(begin, end - begin);
}

////////////////////////////////////////////////////////////////////
//     Function: string_to_int
//  Description: A string-interface wrapper around the C library
//               strtol().  This parses the ASCII representation of an
//               integer, and then sets tail to everything that
//               follows the first valid integer read.  If, on exit,
//               str == tail, there was no valid integer in the
//               source string; if !tail.empty(), there was garbage
//               after the integer.
//
//               It is legal if str and tail refer to the same string.
////////////////////////////////////////////////////////////////////
int
string_to_int(const string &str, string &tail) {
  const char *nptr = str.c_str();
  char *endptr;
  int result = strtol(nptr, &endptr, 10);
  tail = endptr;
  return result;
}

////////////////////////////////////////////////////////////////////
//     Function: string_to_int
//  Description: Another flavor of string_to_int(), this one returns
//               true if the string is a perfectly valid integer (and
//               sets result to that value), or false otherwise.
////////////////////////////////////////////////////////////////////
bool
string_to_int(const string &str, int &result) {
  string tail;
  result = string_to_int(str, tail);
  return tail.empty();
}

////////////////////////////////////////////////////////////////////
//     Function: string_to_double
//  Description: A string-interface wrapper around the C library
//               strtol().  This parses the ASCII representation of an
//               floating-point number, and then sets tail to
//               everything that follows the first valid integer read.
//               If, on exit, str == tail, there was no valid integer
//               in the source string; if !tail.empty(), there was
//               garbage after the number.
//
//               It is legal if str and tail refer to the same string.
////////////////////////////////////////////////////////////////////
double
string_to_double(const string &str, string &tail) {
  const char *nptr = str.c_str();
  char *endptr;
  double result = strtod(nptr, &endptr);
  tail = endptr;
  return result;
}

////////////////////////////////////////////////////////////////////
//     Function: string_to_double
//  Description: Another flavor of string_to_double(), this one
//               returns true if the string is a perfectly valid
//               number (and sets result to that value), or false
//               otherwise.
////////////////////////////////////////////////////////////////////
bool
string_to_double(const string &str, double &result) {
  string tail;
  result = string_to_double(str, tail);
  return tail.empty();
}

////////////////////////////////////////////////////////////////////
//     Function: string_to_float
//  Description: Another flavor of string_to_float(), this one
//               returns true if the string is a perfectly valid
//               number (and sets result to that value), or false
//               otherwise.
////////////////////////////////////////////////////////////////////
bool
string_to_float(const string &str, float &result) {
  string tail;
  result = (float)string_to_double(str, tail);
  return tail.empty();
}
<|endoftext|>