diff --git a/include/boost/geometry/extensions/index/rtree/linear/redistribute_elements.hpp b/include/boost/geometry/extensions/index/rtree/linear/redistribute_elements.hpp index 5e28a2711..6f4b237a8 100644 --- a/include/boost/geometry/extensions/index/rtree/linear/redistribute_elements.hpp +++ b/include/boost/geometry/extensions/index/rtree/linear/redistribute_elements.hpp @@ -224,12 +224,16 @@ struct redistribute_elements::apply(elements_copy, parameters, translator, seed1, seed2); + linear::pick_seeds< + elements_type, + parameters_type, + Translator + >::apply(elements_copy, parameters, translator, seed1, seed2); // prepare nodes' elements containers elements1.clear(); @@ -238,8 +242,8 @@ struct redistribute_elements::apply(elements_copy, allocators); + //elements_copy.clear(); - throw; // RETHROW + throw; // RETHROW } } }; diff --git a/include/boost/geometry/extensions/index/rtree/node/node.hpp b/include/boost/geometry/extensions/index/rtree/node/node.hpp index 30e5b48e0..850192e9f 100644 --- a/include/boost/geometry/extensions/index/rtree/node/node.hpp +++ b/include/boost/geometry/extensions/index/rtree/node/node.hpp @@ -53,6 +53,26 @@ inline Box elements_box(FwdIter first, FwdIter last, Translator const& tr) return result; } +// destroys subtree if the element is internal node's element +template +struct destroy_element +{ + typedef typename Options::parameters_type parameters_type; + + typedef typename rtree::internal_node::type internal_node; + typedef typename rtree::leaf::type leaf; + + typedef rtree::node_auto_ptr node_auto_ptr; + + inline static void apply(typename internal_node::elements_type::value_type & element, Allocators & allocators) + { + node_auto_ptr dummy(element.second, allocators); + element.second = 0; + } + + inline static void apply(typename leaf::elements_type::value_type &, Allocators &) {} +}; + // destroys stored subtrees if internal node's elements are passed template struct destroy_elements diff --git a/include/boost/geometry/extensions/index/rtree/node/node_d_mem_dynamic.hpp b/include/boost/geometry/extensions/index/rtree/node/node_d_mem_dynamic.hpp index 84d4c931b..e66cef8c7 100644 --- a/include/boost/geometry/extensions/index/rtree/node/node_d_mem_dynamic.hpp +++ b/include/boost/geometry/extensions/index/rtree/node/node_d_mem_dynamic.hpp @@ -203,6 +203,8 @@ struct create_dynamic_node try { + // NOTE/TODO + // Here the whole node may be copied alloc_node.construct(p, Node(alloc_elems)); } catch(...) diff --git a/include/boost/geometry/extensions/index/rtree/node/node_s_mem_dynamic.hpp b/include/boost/geometry/extensions/index/rtree/node/node_s_mem_dynamic.hpp index 8eb5d6b8e..6f14beb80 100644 --- a/include/boost/geometry/extensions/index/rtree/node/node_s_mem_dynamic.hpp +++ b/include/boost/geometry/extensions/index/rtree/node/node_s_mem_dynamic.hpp @@ -166,6 +166,8 @@ struct create_static_node try { + // NOTE/TODO + // Here the whole node may be copied alloc_node.construct(p, Node(alloc_elems)); // implicit cast to Variant } catch(...) diff --git a/include/boost/geometry/extensions/index/rtree/quadratic/redistribute_elements.hpp b/include/boost/geometry/extensions/index/rtree/quadratic/redistribute_elements.hpp index a179389e8..b8fb0eb83 100644 --- a/include/boost/geometry/extensions/index/rtree/quadratic/redistribute_elements.hpp +++ b/include/boost/geometry/extensions/index/rtree/quadratic/redistribute_elements.hpp @@ -109,107 +109,127 @@ struct redistribute_elements::apply(elements_copy, parameters, translator, seed1, seed2); + quadratic::pick_seeds< + elements_type, + parameters_type, + Translator, + Box + >::apply(elements_copy, parameters, translator, seed1, seed2); // prepare nodes' elements containers elements1.clear(); BOOST_GEOMETRY_INDEX_ASSERT(elements2.empty(), "second node's elements container should be empty"); - // add seeds - elements1.push_back(elements_copy[seed1]); - elements2.push_back(elements_copy[seed2]); - - // calculate boxes - geometry::convert(rtree::element_indexable(elements_copy[seed1], translator), box1); - geometry::convert(rtree::element_indexable(elements_copy[seed2], translator), box2); - - // remove seeds - if (seed1 < seed2) + try { - elements_copy.erase(elements_copy.begin() + seed2); - elements_copy.erase(elements_copy.begin() + seed1); - } - else - { - elements_copy.erase(elements_copy.begin() + seed1); - elements_copy.erase(elements_copy.begin() + seed2); - } + // add seeds + elements1.push_back(elements_copy[seed1]); // MAY THROW + elements2.push_back(elements_copy[seed2]); // MAY THROW - // initialize areas - content_type content1 = index::content(box1); - content_type content2 = index::content(box2); + // calculate boxes + geometry::convert(rtree::element_indexable(elements_copy[seed1], translator), box1); + geometry::convert(rtree::element_indexable(elements_copy[seed2], translator), box2); - size_t remaining = elements_copy.size(); - - // redistribute the rest of the elements - while ( !elements_copy.empty() ) - { - typename elements_type::reverse_iterator el_it = elements_copy.rbegin(); - bool insert_into_group1 = false; - - size_t elements1_count = elements1.size(); - size_t elements2_count = elements2.size(); - - // if there is small number of elements left and the number of elements in node is lesser than min_elems - // just insert them to this node - if ( elements1_count + remaining <= parameters.get_min_elements() ) + // remove seeds + if (seed1 < seed2) { - insert_into_group1 = true; + elements_copy.erase(elements_copy.begin() + seed2); // MAY THROW + elements_copy.erase(elements_copy.begin() + seed1); // MAY THROW } - else if ( elements2_count + remaining <= parameters.get_min_elements() ) - { - insert_into_group1 = false; - } - // insert the best element else { - // find element with minimum groups areas increses differences - content_type content_increase1 = 0; - content_type content_increase2 = 0; - el_it = pick_next(elements_copy.rbegin(), elements_copy.rend(), - box1, box2, content1, content2, translator, - content_increase1, content_increase2); + elements_copy.erase(elements_copy.begin() + seed1); // MAY THROW + elements_copy.erase(elements_copy.begin() + seed2); // MAY THROW + } - if ( content_increase1 < content_increase2 || - ( content_increase1 == content_increase2 && content1 < content2 ) || - ( content1 == content2 && elements1_count <= elements2_count ) ) + // initialize areas + content_type content1 = index::content(box1); + content_type content2 = index::content(box2); + + size_t remaining = elements_copy.size(); + + // redistribute the rest of the elements + while ( !elements_copy.empty() ) + { + typename elements_type::reverse_iterator el_it = elements_copy.rbegin(); + bool insert_into_group1 = false; + + size_t elements1_count = elements1.size(); + size_t elements2_count = elements2.size(); + + // if there is small number of elements left and the number of elements in node is lesser than min_elems + // just insert them to this node + if ( elements1_count + remaining <= parameters.get_min_elements() ) { insert_into_group1 = true; } - else + else if ( elements2_count + remaining <= parameters.get_min_elements() ) { insert_into_group1 = false; } + // insert the best element + else + { + // find element with minimum groups areas increses differences + content_type content_increase1 = 0; + content_type content_increase2 = 0; + el_it = pick_next(elements_copy.rbegin(), elements_copy.rend(), + box1, box2, content1, content2, translator, + content_increase1, content_increase2); + + if ( content_increase1 < content_increase2 || + ( content_increase1 == content_increase2 && content1 < content2 ) || + ( content1 == content2 && elements1_count <= elements2_count ) ) + { + insert_into_group1 = true; + } + else + { + insert_into_group1 = false; + } + } + + // move element to the choosen group + element_type const& elem = *el_it; + indexable_type const& indexable = rtree::element_indexable(elem, translator); + + if ( insert_into_group1 ) + { + elements1.push_back(elem); // MAY THROW + geometry::expand(box1, indexable); + content1 = index::content(box1); + } + else + { + elements2.push_back(elem); // MAY THROW + geometry::expand(box2, indexable); + content2 = index::content(box2); + } + + BOOST_GEOMETRY_INDEX_ASSERT(!elements_copy.empty(), "expected more elements"); + typename elements_type::iterator el_it_base = el_it.base(); + elements_copy.erase(--el_it_base); // MAY THROW + + BOOST_GEOMETRY_INDEX_ASSERT(0 < remaining, "expected more remaining elements"); + --remaining; } + } + catch(...) + { + //elements_copy.clear(); + elements1.clear(); + elements2.clear(); - // move element to the choosen group - element_type const& elem = *el_it; - indexable_type const& indexable = rtree::element_indexable(elem, translator); + rtree::destroy_elements::apply(elements_backup, allocators); + //elements_backup.clear(); - if ( insert_into_group1 ) - { - elements1.push_back(elem); - geometry::expand(box1, indexable); - content1 = index::content(box1); - } - else - { - elements2.push_back(elem); - geometry::expand(box2, indexable); - content2 = index::content(box2); - } - - BOOST_GEOMETRY_INDEX_ASSERT(!elements_copy.empty(), "expected more elements"); - typename elements_type::iterator el_it_base = el_it.base(); - elements_copy.erase(--el_it_base); - - BOOST_GEOMETRY_INDEX_ASSERT(0 < remaining, "expected more remaining elements"); - --remaining; + throw; // RETHROW } } diff --git a/include/boost/geometry/extensions/index/rtree/rstar/insert.hpp b/include/boost/geometry/extensions/index/rtree/rstar/insert.hpp index d36cec543..1f78cb995 100644 --- a/include/boost/geometry/extensions/index/rtree/rstar/insert.hpp +++ b/include/boost/geometry/extensions/index/rtree/rstar/insert.hpp @@ -37,7 +37,8 @@ public: internal_node *parent, size_t current_child_index, parameters_type const& parameters, - Translator const& translator) + Translator const& translator, + Allocators & allocators) { typedef typename rtree::elements_type::type elements_type; typedef typename elements_type::value_type element_type; @@ -62,15 +63,14 @@ public: typename index::detail::rtree::container_from_elements_type< elements_type, std::pair - >::type sorted_elements(elements_count); - + >::type sorted_elements(elements_count); // MAY THROW + for ( size_t i = 0 ; i < elements_count ; ++i ) { point_type element_center; - geometry::centroid( rtree::element_indexable(elements[i], translator), - element_center); + geometry::centroid( rtree::element_indexable(elements[i], translator), element_center); sorted_elements[i].first = geometry::comparable_distance(node_center, element_center); - sorted_elements[i].second = elements[i]; + sorted_elements[i].second = elements[i]; // MAY THROW } // sort elements by distances from center @@ -78,17 +78,30 @@ public: sorted_elements.begin(), sorted_elements.begin() + reinserted_elements_count, sorted_elements.end(), - distances_dsc); + distances_dsc); // MAY THROW // copy elements which will be reinserted - result_elements.resize(reinserted_elements_count); + result_elements.resize(reinserted_elements_count); // MAY THROW for ( size_t i = 0 ; i < reinserted_elements_count ; ++i ) - result_elements[i] = sorted_elements[i].second; + result_elements[i] = sorted_elements[i].second; // MAY THROW - // copy remaining elements to the current node - elements.resize(elements_count - reinserted_elements_count); - for ( size_t i = reinserted_elements_count ; i < elements_count ; ++i ) - elements[i - reinserted_elements_count] = sorted_elements[i].second; + try + { + // copy remaining elements to the current node + size_t elements_new_count = elements_count - reinserted_elements_count; + elements.resize(elements_new_count); // MIGHT THROW + for ( size_t i = 0 ; i < elements_new_count ; ++i ) + elements[i] = sorted_elements[i + reinserted_elements_count].second; // MAY THROW + } + catch(...) + { + elements.clear(); + + for ( size_t i = 0 ; i < elements_count ; ++i ) + destroy_element::apply(sorted_elements[i].second, allocators); + + throw; // RETHROW + } } private: @@ -161,16 +174,18 @@ struct level_insert_base // node isn't root node if ( !base::m_traverse_data.current_is_root() ) { + // NOTE: exception-safety + // After an exception result_elements may contain garbage rstar::remove_elements_to_reinsert::apply( result_elements, n, base::m_traverse_data.parent, base::m_traverse_data.current_child_index, - base::m_parameters, base::m_translator); + base::m_parameters, base::m_translator, base::m_allocators); // MAY THROW } // node is root node else { BOOST_GEOMETRY_INDEX_ASSERT(&n == rtree::get(base::m_root_node), "node should be the root node"); - base::split(n); + base::split(n); // MAY THROW } } } @@ -181,7 +196,7 @@ struct level_insert_base // overflow if ( base::m_parameters.get_max_elements() < rtree::elements(n).size() ) { - base::split(n); + base::split(n); // MAY THROW } } @@ -228,7 +243,7 @@ struct level_insert if ( base::m_traverse_data.current_level < base::m_level ) { // next traversing step - base::traverse(*this, n); + base::traverse(*this, n); // MAY THROW // further insert if ( 0 < InsertIndex ) @@ -237,7 +252,7 @@ struct level_insert if ( base::m_traverse_data.current_level == base::m_level - 1 ) { - base::handle_possible_reinsert_or_split_of_root(n); + base::handle_possible_reinsert_or_split_of_root(n); // MAY THROW } } } @@ -246,17 +261,17 @@ struct level_insert BOOST_GEOMETRY_INDEX_ASSERT(base::m_level == base::m_traverse_data.current_level, "unexpected level"); // push new child node - rtree::elements(n).push_back(base::m_element); + rtree::elements(n).push_back(base::m_element); // MAY THROW // first insert if ( 0 == InsertIndex ) { - base::handle_possible_reinsert_or_split_of_root(n); + base::handle_possible_reinsert_or_split_of_root(n); // MAY THROW } // not the first insert else { - base::handle_possible_split(n); + base::handle_possible_split(n); // MAY THROW } } @@ -296,13 +311,13 @@ struct level_insert::max)(), "unexpected level"); - rtree::elements(n).push_back(base::m_element); + rtree::elements(n).push_back(base::m_element); // MAY THROW - base::handle_possible_split(n); + base::handle_possible_split(n); // MAY THROW } }; @@ -351,7 +366,7 @@ struct level_insert<0, Value, Value, Options, Translator, Box, Allocators> "unexpected level"); // next traversing step - base::traverse(*this, n); + base::traverse(*this, n); // MAY THROW base::recalculate_aabb_if_necessary(n); } @@ -364,11 +379,11 @@ struct level_insert<0, Value, Value, Options, Translator, Box, Allocators> base::m_level == (std::numeric_limits::max)(), "unexpected level"); - rtree::elements(n).push_back(base::m_element); + rtree::elements(n).push_back(base::m_element); // MAY THROW - base::handle_possible_reinsert_or_split_of_root(n); - - base::recalculate_aabb_if_necessary(n); + base::handle_possible_reinsert_or_split_of_root(n); // MAY THROW + + base::recalculate_aabb_if_necessary(n); } }; @@ -408,11 +423,11 @@ public: detail::rstar::level_insert<0, Element, Value, Options, Translator, Box, Allocators> lins_v( m_root, m_leafs_level, m_element, m_parameters, m_translator, m_allocators, m_relative_level); - rtree::apply_visitor(lins_v, *m_root); + rtree::apply_visitor(lins_v, *m_root); // MAY THROW if ( !lins_v.result_elements.empty() ) { - recursive_reinsert(lins_v.result_elements, lins_v.result_relative_level); + recursive_reinsert(lins_v.result_elements, lins_v.result_relative_level); // MAY THROW } } @@ -423,7 +438,7 @@ public: detail::rstar::level_insert<0, Element, Value, Options, Translator, Box, Allocators> lins_v( m_root, m_leafs_level, m_element, m_parameters, m_translator, m_allocators, m_relative_level); - rtree::apply_visitor(lins_v, *m_root); + rtree::apply_visitor(lins_v, *m_root); // MAY THROW // we're in the root, so root should be split and there should be no elements to reinsert assert(lins_v.result_elements.empty()); @@ -442,14 +457,14 @@ private: detail::rstar::level_insert<1, element_type, Value, Options, Translator, Box, Allocators> lins_v( m_root, m_leafs_level, *it, m_parameters, m_translator, m_allocators, relative_level); - rtree::apply_visitor(lins_v, *m_root); + rtree::apply_visitor(lins_v, *m_root); // MAY THROW assert(relative_level + 1 == lins_v.result_relative_level); // non-root relative level if ( lins_v.result_relative_level < m_leafs_level && !lins_v.result_elements.empty()) { - recursive_reinsert(lins_v.result_elements, lins_v.result_relative_level); + recursive_reinsert(lins_v.result_elements, lins_v.result_relative_level); // MAY THROW } } } diff --git a/include/boost/geometry/extensions/index/rtree/rstar/redistribute_elements.hpp b/include/boost/geometry/extensions/index/rtree/rstar/redistribute_elements.hpp index 0520057a8..469f1c702 100644 --- a/include/boost/geometry/extensions/index/rtree/rstar/redistribute_elements.hpp +++ b/include/boost/geometry/extensions/index/rtree/rstar/redistribute_elements.hpp @@ -68,11 +68,11 @@ struct choose_split_axis_and_index_for_corner BOOST_GEOMETRY_INDEX_ASSERT(elements.size() == parameters.get_max_elements() + 1, "wrong number of elements"); // copy elements - Elements elements_copy = elements; + Elements elements_copy(elements); // MAY THROW // sort elements element_axis_corner_less elements_less(translator); - std::sort(elements_copy.begin(), elements_copy.end(), elements_less); + std::sort(elements_copy.begin(), elements_copy.end(), elements_less); // MAY THROW // init outputs choosen_index = parameters.get_min_elements(); @@ -135,7 +135,7 @@ struct choose_split_axis_and_index_for_axis choose_split_axis_and_index_for_corner:: apply(elements, index1, som1, ovl1, con1, - parameters, translator); + parameters, translator); // MAY THROW size_t index2 = 0; margin_type som2 = 0; @@ -145,7 +145,7 @@ struct choose_split_axis_and_index_for_axis choose_split_axis_and_index_for_corner:: apply(elements, index2, som2, ovl2, con2, - parameters, translator); + parameters, translator); // MAY THROW sum_of_margins = som1 + som2; @@ -185,7 +185,7 @@ struct choose_split_axis_and_index_for_axis:: apply(elements, choosen_index, sum_of_margins, smallest_overlap, smallest_content, - parameters, translator); + parameters, translator); // MAY THROW choosen_corner = min_corner; } @@ -215,7 +215,7 @@ struct choose_split_axis_and_index choose_split_axis_and_index:: apply(elements, choosen_axis, choosen_corner, choosen_index, smallest_sum_of_margins, smallest_overlap, smallest_content, - parameters, translator); + parameters, translator); // MAY THROW margin_type sum_of_margins = 0; @@ -230,7 +230,7 @@ struct choose_split_axis_and_index Box, Dimension - 1, typename index::traits::tag::type - >::apply(elements, corner, index, sum_of_margins, overlap_val, content_val, parameters, translator); + >::apply(elements, corner, index, sum_of_margins, overlap_val, content_val, parameters, translator); // MAY THROW if ( sum_of_margins < smallest_sum_of_margins ) { @@ -270,7 +270,7 @@ struct choose_split_axis_and_index Box, 0, typename index::traits::tag::type - >::apply(elements, choosen_corner, choosen_index, smallest_sum_of_margins, smallest_overlap, smallest_content, parameters, translator); + >::apply(elements, choosen_corner, choosen_index, smallest_sum_of_margins, smallest_overlap, smallest_content, parameters, translator); // MAY THROW } }; @@ -284,7 +284,7 @@ struct partial_sort { if ( axis < Dimension - 1 ) { - partial_sort::apply(elements, axis, index, tr); + partial_sort::apply(elements, axis, index, tr); // MAY THROW } else { @@ -292,7 +292,7 @@ struct partial_sort typedef typename Elements::value_type element_type; element_axis_corner_less less(tr); - std::partial_sort(elements.begin(), elements.begin() + index, elements.end(), less); + std::partial_sort(elements.begin(), elements.begin() + index, elements.end(), less); // MAY THROW } } }; @@ -307,7 +307,7 @@ struct partial_sort typedef typename Elements::value_type element_type; element_axis_corner_less less(tr); - std::partial_sort(elements.begin(), elements.begin() + index, elements.end(), less); + std::partial_sort(elements.begin(), elements.begin() + index, elements.end(), less); // MAY THROW } }; @@ -349,11 +349,14 @@ struct redistribute_elements::max)(); content_type smallest_content = (std::numeric_limits::max)(); - rstar::choose_split_axis_and_index::value>:: - apply(elements1, - split_axis, split_corner, split_index, - smallest_sum_of_margins, smallest_overlap, smallest_content, - parameters, translator); + rstar::choose_split_axis_and_index< + typename Options::parameters_type, + Box, + index::traits::dimension::value + >::apply(elements1, + split_axis, split_corner, split_index, + smallest_sum_of_margins, smallest_overlap, smallest_content, + parameters, translator); // MAY THROW // TODO: awulkiew - get rid of following static_casts? @@ -361,20 +364,39 @@ struct redistribute_elements(min_corner) || split_corner == static_cast(max_corner), "unexpected value"); BOOST_GEOMETRY_INDEX_ASSERT(parameters.get_min_elements() <= split_index && split_index <= parameters.get_max_elements() - parameters.get_min_elements() + 1, "unexpected value"); + // copy original elements + elements_type elements_copy(elements1); // MAY THROW + // TODO: awulkiew - check if std::partial_sort produces the same result as std::sort if ( split_corner == static_cast(min_corner) ) - rstar::partial_sort::apply(elements1, split_axis, split_index, translator); + rstar::partial_sort + ::apply(elements_copy, split_axis, split_index, translator); // MAY THROW else - rstar::partial_sort::apply(elements1, split_axis, split_index, translator); + rstar::partial_sort + ::apply(elements_copy, split_axis, split_index, translator); // MAY THROW - // copy elements to node 2 and remove from node 1 - elements2.resize(parameters.get_max_elements() + 1 - split_index); - std::copy(elements1.begin() + split_index, elements1.end(), elements2.begin()); - elements1.resize(split_index); + try + { + // copy elements to nodes + elements1.resize(split_index); // MIGHT THROW + std::copy(elements_copy.begin(), elements_copy.begin() + split_index, elements1.begin()); // MAY THROW + elements2.resize(parameters.get_max_elements() + 1 - split_index); // MAY THROW + std::copy(elements_copy.begin() + split_index, elements_copy.end(), elements2.begin()); // MAY THROW - // calculate boxes - box1 = rtree::elements_box(elements1.begin(), elements1.end(), translator); - box2 = rtree::elements_box(elements2.begin(), elements2.end(), translator); + // calculate boxes + box1 = rtree::elements_box(elements1.begin(), elements1.end(), translator); + box2 = rtree::elements_box(elements2.begin(), elements2.end(), translator); + } + catch(...) + { + elements1.clear(); + elements2.clear(); + + rtree::destroy_elements::apply(elements_copy, allocators); + //elements_copy.clear(); + + throw; // RETHROW + } } }; diff --git a/include/boost/geometry/extensions/index/rtree/rtree.hpp b/include/boost/geometry/extensions/index/rtree/rtree.hpp index d4c2db3bc..d21b74034 100644 --- a/include/boost/geometry/extensions/index/rtree/rtree.hpp +++ b/include/boost/geometry/extensions/index/rtree/rtree.hpp @@ -277,27 +277,20 @@ public: { BOOST_GEOMETRY_INDEX_ASSERT(index::is_valid(m_translator(value)), "Indexable is invalid"); - try - { - detail::rtree::visitors::insert< - value_type, - value_type, - options_type, - translator_type, - box_type, - allocators_type, - typename options_type::insert_tag - > insert_v(m_root, m_leafs_level, value, m_parameters, m_translator, m_allocators); + detail::rtree::visitors::insert< + value_type, + value_type, + options_type, + translator_type, + box_type, + allocators_type, + typename options_type::insert_tag + > insert_v(m_root, m_leafs_level, value, m_parameters, m_translator, m_allocators); - detail::rtree::apply_visitor(insert_v, *m_root); + detail::rtree::apply_visitor(insert_v, *m_root); - ++m_values_count; - } - catch(...) - { - this->destroy(*this); - throw; - } + // If exception is thrown, m_values_count is invalid + ++m_values_count; } /*! diff --git a/include/boost/geometry/extensions/index/rtree/tags.hpp b/include/boost/geometry/extensions/index/rtree/tags.hpp index 7170755a2..5589be5b6 100644 --- a/include/boost/geometry/extensions/index/rtree/tags.hpp +++ b/include/boost/geometry/extensions/index/rtree/tags.hpp @@ -1,6 +1,6 @@ // Boost.Geometry Index // -// Tags used by the R-tree implementation. +// Tags used by the R-tree predicates implementation. // // Copyright (c) 2011-2012 Adam Wulkiewicz, Lodz, Poland. // diff --git a/include/boost/geometry/extensions/index/rtree/visitors/destroy.hpp b/include/boost/geometry/extensions/index/rtree/visitors/destroy.hpp index 7be482930..567e66d84 100644 --- a/include/boost/geometry/extensions/index/rtree/visitors/destroy.hpp +++ b/include/boost/geometry/extensions/index/rtree/visitors/destroy.hpp @@ -42,10 +42,11 @@ public: elements_type & elements = rtree::elements(n); for (typename elements_type::iterator it = elements.begin(); - it != elements.end(); ++it) + it != elements.end(); ++it) { m_current_node = it->second; rtree::apply_visitor(*this, *m_current_node); + it->second = 0; } rtree::destroy_node::apply(m_allocators, node_to_destroy); diff --git a/include/boost/geometry/extensions/index/rtree/visitors/insert.hpp b/include/boost/geometry/extensions/index/rtree/visitors/insert.hpp index 22f93954a..d3f929403 100644 --- a/include/boost/geometry/extensions/index/rtree/visitors/insert.hpp +++ b/include/boost/geometry/extensions/index/rtree/visitors/insert.hpp @@ -139,11 +139,14 @@ public: // create reference to the newly created node Node & n2 = rtree::get(*second_node); - // After throwing an exception by redistribute_elements both nodes may be empty. - // The tree won't be valid r-tree. + // NOTE: thread-safety + // After throwing an exception by redistribute_elements the original node may be not changed or + // both nodes may be empty. In both cases the tree won't be valid r-tree. // The alternative is to create 2 (or more) additional nodes here and store backup info - // in the original node, but then, if exception was thrown, the node would have more than max - // elements which also is not allowed in the r-tree. + // in the original node, then, if exception was thrown, the node would always have more than max + // elements. + // The alternative is to use moving semantics in the implementations of redistribute_elements, + // it will be possible to throw from boost::move() in the case of e.g. static size nodes. // redistribute elements Box box2; @@ -266,7 +269,7 @@ protected: rtree::element_indexable(m_element, m_translator)); // next traversing step - traverse_apply_visitor(visitor, n, choosen_node_index); + traverse_apply_visitor(visitor, n, choosen_node_index); // MAY THROW } // TODO: awulkiew - change post_traverse name to handle_overflow or overflow_treatment? @@ -295,7 +298,7 @@ protected: m_traverse_data.move_to_next_level(&n, choosen_node_index); // next traversing step - rtree::apply_visitor(visitor, *rtree::elements(n)[choosen_node_index].second); + rtree::apply_visitor(visitor, *rtree::elements(n)[choosen_node_index].second); // MAY THROW // restore previous traverse inputs m_traverse_data = backup_traverse_data; @@ -415,7 +418,7 @@ public: if ( base::m_traverse_data.current_level < base::m_level ) { // next traversing step - base::traverse(*this, n); + base::traverse(*this, n); // MAY THROW } else { @@ -464,7 +467,7 @@ public: BOOST_GEOMETRY_INDEX_ASSERT(base::m_traverse_data.current_level < base::m_level, "unexpected level"); // next traversing step - base::traverse(*this, n); + base::traverse(*this, n); // MAY THROW base::post_traverse(n); // MAY THROW } diff --git a/test/rtree/Jamfile.v2 b/test/rtree/Jamfile.v2 index b4bca41d0..c424bcf21 100644 --- a/test/rtree/Jamfile.v2 +++ b/test/rtree/Jamfile.v2 @@ -37,5 +37,7 @@ test-suite boost-geometry-index-rtree [ run rtree3d_rstar_f.cpp ] [ run rtree3d_rstar_d.cpp ] [ run rtree3d_rstar_tt.cpp ] + + [ run rtree_exceptions.cpp ] ; diff --git a/test/rtree/rtree_exceptions.cpp b/test/rtree/rtree_exceptions.cpp new file mode 100644 index 000000000..c388d04ba --- /dev/null +++ b/test/rtree/rtree_exceptions.cpp @@ -0,0 +1,51 @@ +// Boost.Geometry Index +// Unit Test + +// Copyright (c) 2011-2012 Adam Wulkiewicz, Lodz, Poland. + +// Use, modification and distribution is subject to the Boost Software License, +// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include + +#include +#include +#include + +// test value exceptions +template +void test_rtree_value_exceptions(Parameters const& parameters = Parameters()) +{ + typedef std::pair, throwing_value> Value; + typedef bgi::rtree Tree; + typedef typename Tree::box_type B; + + for ( size_t i = 10 ; i < 100 ; i += 10 ) + { + throwing_value::reset_calls_counter(); + throwing_value::set_max_calls((std::numeric_limits::max)()); + + Tree tree(parameters); + std::vector input; + B qbox; + generate_input<2>::apply(input, qbox); + + throwing_value::reset_calls_counter(); + throwing_value::set_max_calls(0); + + BOOST_CHECK_THROW( tree.insert(input.begin(), input.end()), throwing_value_copy_exception ); + } +} + +int test_main(int, char* []) +{ + test_rtree_value_exceptions< bgi::linear<4, 2> >(); + test_rtree_value_exceptions(bgi::runtime::linear(4, 2)); + test_rtree_value_exceptions< bgi::quadratic<4, 2> >(); + test_rtree_value_exceptions(bgi::runtime::quadratic(4, 2)); + test_rtree_value_exceptions< bgi::rstar<4, 2> >(); + test_rtree_value_exceptions(bgi::runtime::rstar(4, 2)); + + return 0; +} diff --git a/test/rtree/test_rtree_exceptions.hpp b/test/rtree/test_rtree_exceptions.hpp new file mode 100644 index 000000000..20e6fefbe --- /dev/null +++ b/test/rtree/test_rtree_exceptions.hpp @@ -0,0 +1,291 @@ +// Boost.Geometry Index +// +// R-tree nodes based on runtime-polymorphism, storing static-size containers +// test version throwing exceptions on creation +// +// Copyright (c) 2011-2012 Adam Wulkiewicz, Lodz, Poland. +// +// Use, modification and distribution is subject to the Boost Software License, +// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_GEOMETRY_EXTENSIONS_INDEX_TEST_RTREE_EXCEPTIONS_HPP +#define BOOST_GEOMETRY_EXTENSIONS_INDEX_TEST_RTREE_EXCEPTIONS_HPP + +#include + +#include +#include + +namespace boost { namespace geometry { namespace index { + +// options implementation (from options.hpp) + +struct node_throwing_d_mem_static_tag {}; + +template +struct linear_throwing : public linear {}; + +template +struct quadratic_throwing : public quadratic {}; + +template ::value> +struct rstar_throwing : public rstar {}; + +namespace detail { namespace rtree { + +template +struct options_type< linear_throwing > +{ + typedef options::rtree< + linear_throwing, + insert_default_tag, choose_by_content_diff_tag, split_default_tag, linear_tag, + node_throwing_d_mem_static_tag + > type; +}; + +template +struct options_type< quadratic_throwing > +{ + typedef options::rtree< + quadratic_throwing, + insert_default_tag, choose_by_content_diff_tag, split_default_tag, quadratic_tag, + node_throwing_d_mem_static_tag + > type; +}; + +template +struct options_type< rstar_throwing > +{ + typedef options::rtree< + rstar_throwing, + insert_reinsert_tag, choose_by_overlap_diff_tag, split_default_tag, rstar_tag, + node_throwing_d_mem_static_tag + > type; +}; + +}} // namespace detail::rtree + +// node implementation + +namespace detail { namespace rtree { + +template +struct dynamic_internal_node + : public dynamic_node +{ + typedef index::pushable_array< + std::pair< + Box, + dynamic_node * + >, + Parameters::max_elements + 1 + > elements_type; + + template + inline dynamic_internal_node(Dummy) {} + + void apply_visitor(dynamic_visitor & v) { v(*this); } + void apply_visitor(dynamic_visitor & v) const { v(*this); } + + elements_type elements; +}; + +template +struct dynamic_leaf + : public dynamic_node +{ + typedef index::pushable_array elements_type; + + template + inline dynamic_leaf(Dummy) {} + + void apply_visitor(dynamic_visitor & v) { v(*this); } + void apply_visitor(dynamic_visitor & v) const { v(*this); } + + elements_type elements; +}; + +// nodes traits + +template +struct node +{ + typedef dynamic_node type; +}; + +template +struct internal_node +{ + typedef dynamic_internal_node type; +}; + +template +struct leaf +{ + typedef dynamic_leaf type; +}; + +template +struct visitor +{ + typedef dynamic_visitor type; +}; + +// allocators + +template +struct allocators +{ + typedef Allocator allocator_type; + typedef typename allocator_type::size_type size_type; + + typedef typename allocator_type::template rebind< + typename internal_node::type + >::other internal_node_allocator_type; + + typedef typename allocator_type::template rebind< + typename leaf::type + >::other leaf_allocator_type; + + inline explicit allocators(Allocator alloc) + : allocator(alloc) + , internal_node_allocator(allocator) + , leaf_allocator(allocator) + {} + + allocator_type allocator; + internal_node_allocator_type internal_node_allocator; + leaf_allocator_type leaf_allocator; +}; + +struct internal_node_bad_alloc : public std::exception +{ + const char * what() { return "internal node creation failed."; } +}; + +// create_node + +template +struct create_node< + Allocators, + dynamic_internal_node +> +{ + static inline dynamic_node * + apply(Allocators & allocators) + { + // throw if counter meets max count + if ( get_max_calls_ref() <= get_calls_counter_ref() ) + throw internal_node_bad_alloc(); + else + ++get_calls_counter_ref(); + + return create_dynamic_node< + dynamic_node, + dynamic_internal_node + >::template apply(allocators.internal_node_allocator, allocators.internal_node_allocator); + } + + static void reset_calls_counter() { get_calls_counter_ref() = 0; } + static void set_max_calls(size_t mc) { get_max_calls_ref() = mc; } + + static size_t & get_calls_counter_ref() { static size_t cc = 0; return cc; } + static size_t & get_max_calls_ref() { static size_t mc = 0; return mc; } +}; + +struct leaf_bad_alloc : public std::exception +{ + const char * what() { return "leaf node creation failed."; } +}; + +template +struct create_node< + Allocators, + dynamic_leaf +> +{ + static inline typename node::type * + apply(Allocators & allocators) + { + // throw if counter meets max count + if ( get_max_calls_ref() <= get_calls_counter_ref() ) + throw leaf_bad_alloc(); + else + ++get_calls_counter_ref(); + + return create_dynamic_node< + dynamic_node, + dynamic_leaf + >::template apply(allocators.leaf_allocator, allocators.leaf_allocator); + } + + static void reset_calls_counter() { get_calls_counter_ref() = 0; } + static void set_max_calls(size_t mc) { get_max_calls_ref() = mc; } + + static size_t & get_calls_counter_ref() { static size_t cc = 0; return cc; } + static size_t & get_max_calls_ref() { static size_t mc = 0; return mc; } +}; + +}} // namespace detail::rtree + +}}} // namespace boost::geometry::index + +// value implementation + +struct throwing_value_copy_exception : public std::exception +{ + const char * what() { return "value copy failed."; } +}; + +struct throwing_value +{ + explicit throwing_value(int v = 0) + : value(v) + {} + + throwing_value(throwing_value const& v) + { + throw_if_required(); + + value = v.value; + } + + throwing_value & operator=(throwing_value const& v) + { + throw_if_required(); + + value = v.value; + return *this; + } + + void throw_if_required() + { + // throw if counter meets max count + if ( get_max_calls_ref() <= get_calls_counter_ref() ) + throw throwing_value_copy_exception(); + else + ++get_calls_counter_ref(); + } + + static void reset_calls_counter() { get_calls_counter_ref() = 0; } + static void set_max_calls(size_t mc) { get_max_calls_ref() = mc; } + + static size_t & get_calls_counter_ref() { static size_t cc = 0; return cc; } + static size_t & get_max_calls_ref() { static size_t mc = 0; return mc; } + + int value; +}; + +template +struct generate_value< std::pair, throwing_value> > +{ + typedef bg::model::point P; + typedef std::pair R; + static R apply(int x, int y) + { + return std::make_pair(P(x, y), throwing_value(x + y * 100)); + } +}; + +#endif // BOOST_GEOMETRY_EXTENSIONS_INDEX_TEST_RTREE_EXCEPTIONS_HPP