760 lines
24 KiB
Plaintext
760 lines
24 KiB
Plaintext
// (C) Copyright Joel de Guzman 2003.
|
|
// Distributed under 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 INDEXING_SUITE_DETAIL_JDG20036_HPP
|
|
# define INDEXING_SUITE_DETAIL_JDG20036_HPP
|
|
|
|
# include <boost/python/extract.hpp>
|
|
# include <boost/scoped_ptr.hpp>
|
|
# include <boost/get_pointer.hpp>
|
|
# include <boost/detail/binary_search.hpp>
|
|
# include <boost/numeric/conversion/cast.hpp>
|
|
# include <boost/type_traits/is_pointer.hpp>
|
|
# include <vector>
|
|
# include <map>
|
|
#include <iostream>
|
|
|
|
namespace boost { namespace python { namespace detail {
|
|
|
|
#if defined(NDEBUG)
|
|
#define BOOST_PYTHON_INDEXING_CHECK_INVARIANT
|
|
#else
|
|
#define BOOST_PYTHON_INDEXING_CHECK_INVARIANT check_invariant()
|
|
#endif
|
|
|
|
template <class Proxy>
|
|
struct compare_proxy_index
|
|
{
|
|
// This functor compares a proxy and an index.
|
|
// This is used by proxy_group::first_proxy to
|
|
// get first proxy with index i.
|
|
|
|
template <class Index>
|
|
bool operator()(PyObject* prox, Index i) const
|
|
{
|
|
typedef typename Proxy::policies_type policies_type;
|
|
Proxy& proxy = extract<Proxy&>(prox)();
|
|
return policies_type::
|
|
compare_index(proxy.get_container(), proxy.get_index(), i);
|
|
}
|
|
};
|
|
|
|
// The proxy_group class holds a vector of container element
|
|
// proxies. First, what is a container element proxy? A container
|
|
// element proxy acts like a smart pointer holding a reference to
|
|
// a container and an index (see container_element, for details).
|
|
//
|
|
// The proxies are held in a vector always sorted by its index.
|
|
// Various functions manage the addition, removal and searching
|
|
// of proxies from the vector.
|
|
//
|
|
template <class Proxy>
|
|
class proxy_group
|
|
{
|
|
public:
|
|
|
|
typedef typename std::vector<PyObject*>::const_iterator const_iterator;
|
|
typedef typename std::vector<PyObject*>::iterator iterator;
|
|
typedef typename Proxy::index_type index_type;
|
|
typedef typename Proxy::policies_type policies_type;
|
|
|
|
iterator
|
|
first_proxy(index_type i)
|
|
{
|
|
// Return the first proxy with index <= i
|
|
return boost::detail::lower_bound(
|
|
proxies.begin(), proxies.end(),
|
|
i, compare_proxy_index<Proxy>());
|
|
}
|
|
|
|
void
|
|
remove(Proxy& proxy)
|
|
{
|
|
// Remove a proxy
|
|
for (iterator iter = first_proxy(proxy.get_index());
|
|
iter != proxies.end(); ++iter)
|
|
{
|
|
if (&extract<Proxy&>(*iter)() == &proxy)
|
|
{
|
|
proxies.erase(iter);
|
|
break;
|
|
}
|
|
}
|
|
BOOST_PYTHON_INDEXING_CHECK_INVARIANT;
|
|
}
|
|
|
|
void
|
|
add(PyObject* prox)
|
|
{
|
|
BOOST_PYTHON_INDEXING_CHECK_INVARIANT;
|
|
// Add a proxy
|
|
proxies.insert(
|
|
first_proxy(extract<Proxy&>(prox)().get_index()), prox);
|
|
BOOST_PYTHON_INDEXING_CHECK_INVARIANT;
|
|
}
|
|
|
|
void
|
|
erase(index_type i, mpl::false_)
|
|
{
|
|
BOOST_PYTHON_INDEXING_CHECK_INVARIANT;
|
|
// Erase the proxy with index i
|
|
replace(i, i+1, 0);
|
|
BOOST_PYTHON_INDEXING_CHECK_INVARIANT;
|
|
}
|
|
|
|
void
|
|
erase(index_type i, mpl::true_)
|
|
{
|
|
BOOST_PYTHON_INDEXING_CHECK_INVARIANT;
|
|
// Erase the proxy with index i
|
|
|
|
iterator iter = first_proxy(i);
|
|
extract<Proxy&> p(*iter);
|
|
|
|
if (iter != proxies.end() && p().get_index() == i)
|
|
{
|
|
extract<Proxy&> p(*iter);
|
|
p().detach();
|
|
proxies.erase(iter);
|
|
}
|
|
BOOST_PYTHON_INDEXING_CHECK_INVARIANT;
|
|
}
|
|
|
|
void
|
|
erase(index_type from, index_type to)
|
|
{
|
|
// note: this cannot be called when container is not sliceable
|
|
|
|
BOOST_PYTHON_INDEXING_CHECK_INVARIANT;
|
|
// Erase all proxies with indexes from..to
|
|
replace(from, to, 0);
|
|
BOOST_PYTHON_INDEXING_CHECK_INVARIANT;
|
|
}
|
|
|
|
void
|
|
replace(
|
|
index_type from,
|
|
index_type to,
|
|
typename std::vector<PyObject*>::size_type len)
|
|
{
|
|
// note: this cannot be called when container is not sliceable
|
|
|
|
BOOST_PYTHON_INDEXING_CHECK_INVARIANT;
|
|
// Erase all proxies with indexes from..to.
|
|
// Adjust the displaced indexes such that the
|
|
// final effect is that we have inserted *len*
|
|
// number of proxies in the vacated region. This
|
|
// procedure involves adjusting the indexes of
|
|
// the proxies.
|
|
|
|
iterator left = first_proxy(from);
|
|
iterator right = proxies.end(); // we'll adjust this later
|
|
|
|
for (iterator iter = left; iter != right; ++iter)
|
|
{
|
|
if (extract<Proxy&>(*iter)().get_index() > to)
|
|
{
|
|
right = iter; // adjust right
|
|
break;
|
|
}
|
|
extract<Proxy&> p(*iter);
|
|
p().detach();
|
|
}
|
|
|
|
typename std::vector<PyObject*>::size_type
|
|
offset = left-proxies.begin();
|
|
proxies.erase(left, right);
|
|
right = proxies.begin()+offset;
|
|
|
|
while (right != proxies.end())
|
|
{
|
|
typedef typename Proxy::container_type::difference_type difference_type;
|
|
extract<Proxy&> p(*right);
|
|
p().set_index(
|
|
extract<Proxy&>(*right)().get_index()
|
|
- (difference_type(to) - from - len)
|
|
);
|
|
|
|
++right;
|
|
}
|
|
BOOST_PYTHON_INDEXING_CHECK_INVARIANT;
|
|
}
|
|
|
|
PyObject*
|
|
find(index_type i)
|
|
{
|
|
BOOST_PYTHON_INDEXING_CHECK_INVARIANT;
|
|
// Find the proxy with *exact* index i.
|
|
// Return 0 (null) if no proxy with the
|
|
// given index is found.
|
|
iterator iter = first_proxy(i);
|
|
if (iter != proxies.end()
|
|
&& extract<Proxy&>(*iter)().get_index() == i)
|
|
{
|
|
BOOST_PYTHON_INDEXING_CHECK_INVARIANT;
|
|
return *iter;
|
|
}
|
|
BOOST_PYTHON_INDEXING_CHECK_INVARIANT;
|
|
return 0;
|
|
}
|
|
|
|
typename std::vector<PyObject*>::size_type
|
|
size() const
|
|
{
|
|
BOOST_PYTHON_INDEXING_CHECK_INVARIANT;
|
|
// How many proxies are there so far?
|
|
return proxies.size();
|
|
}
|
|
|
|
private:
|
|
|
|
#if !defined(NDEBUG)
|
|
void
|
|
check_invariant() const
|
|
{
|
|
for (const_iterator i = proxies.begin(); i != proxies.end(); ++i)
|
|
{
|
|
if ((*i)->ob_refcnt <= 0)
|
|
{
|
|
PyErr_SetString(PyExc_RuntimeError,
|
|
"Invariant: Proxy vector in an inconsistent state");
|
|
throw_error_already_set();
|
|
}
|
|
|
|
if (i+1 != proxies.end())
|
|
{
|
|
if (extract<Proxy&>(*(i+1))().get_index() ==
|
|
extract<Proxy&>(*(i))().get_index())
|
|
{
|
|
PyErr_SetString(PyExc_RuntimeError,
|
|
"Invariant: Proxy vector in an inconsistent state (duplicate proxy)");
|
|
throw_error_already_set();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
std::vector<PyObject*> proxies;
|
|
};
|
|
|
|
// proxy_links holds a map of Container pointers (keys)
|
|
// with proxy_group(s) (data). Various functions manage
|
|
// the addition, removal and searching of proxies from
|
|
// the map.
|
|
//
|
|
template <class Proxy, class Container>
|
|
class proxy_links
|
|
{
|
|
public:
|
|
|
|
typedef std::map<Container*, proxy_group<Proxy> > links_t;
|
|
typedef typename Proxy::index_type index_type;
|
|
|
|
void
|
|
remove(Proxy& proxy)
|
|
{
|
|
// Remove a proxy.
|
|
typename links_t::iterator r = links.find(&proxy.get_container());
|
|
if (r != links.end())
|
|
{
|
|
r->second.remove(proxy);
|
|
if (r->second.size() == 0)
|
|
links.erase(r);
|
|
}
|
|
}
|
|
|
|
void
|
|
add(PyObject* prox, Container& container)
|
|
{
|
|
// Add a proxy
|
|
links[&container].add(prox);
|
|
}
|
|
|
|
template <class NoSlice>
|
|
void erase(Container& container, index_type i, NoSlice no_slice)
|
|
{
|
|
// Erase the proxy with index i
|
|
typename links_t::iterator r = links.find(&container);
|
|
if (r != links.end())
|
|
{
|
|
r->second.erase(i, no_slice);
|
|
if (r->second.size() == 0)
|
|
links.erase(r);
|
|
}
|
|
}
|
|
|
|
void
|
|
erase(Container& container, index_type from, index_type to)
|
|
{
|
|
// Erase all proxies with indexes from..to
|
|
typename links_t::iterator r = links.find(&container);
|
|
if (r != links.end())
|
|
{
|
|
r->second.erase(from, to);
|
|
if (r->second.size() == 0)
|
|
links.erase(r);
|
|
}
|
|
}
|
|
|
|
void
|
|
replace(
|
|
Container& container,
|
|
index_type from, index_type to, index_type len)
|
|
{
|
|
// Erase all proxies with indexes from..to.
|
|
// Adjust the displaced indexes such that the
|
|
// final effect is that we have inserted *len*
|
|
// number of proxies in the vacated region. This
|
|
// procedure involves adjusting the indexes of
|
|
// the proxies.
|
|
|
|
typename links_t::iterator r = links.find(&container);
|
|
if (r != links.end())
|
|
{
|
|
r->second.replace(from, to, len);
|
|
if (r->second.size() == 0)
|
|
links.erase(r);
|
|
}
|
|
}
|
|
|
|
PyObject*
|
|
find(Container& container, index_type i)
|
|
{
|
|
// Find the proxy with *exact* index i.
|
|
// Return 0 (null) if no proxy with the given
|
|
// index is found.
|
|
typename links_t::iterator r = links.find(&container);
|
|
if (r != links.end())
|
|
return r->second.find(i);
|
|
return 0;
|
|
}
|
|
|
|
private:
|
|
|
|
links_t links;
|
|
};
|
|
|
|
// container_element is our container proxy class.
|
|
// This class acts like a smart pointer to a container
|
|
// element. The class holds an index and a reference to
|
|
// a container. Dereferencing the smart pointer will
|
|
// retrieve the nth (index) element from the container.
|
|
//
|
|
// A container_element can also be detached from the
|
|
// container. In such a detached state, the container_element
|
|
// holds a copy of the nth (index) element, which it
|
|
// returns when dereferenced.
|
|
//
|
|
template <class Container, class Index, class Policies>
|
|
class container_element
|
|
{
|
|
public:
|
|
|
|
typedef Index index_type;
|
|
typedef Container container_type;
|
|
typedef typename Policies::data_type element_type;
|
|
typedef Policies policies_type;
|
|
typedef container_element<Container, Index, Policies> self_t;
|
|
typedef proxy_group<self_t> links_type;
|
|
|
|
container_element(object container, Index index)
|
|
: ptr()
|
|
, container(container)
|
|
, index(index)
|
|
{
|
|
}
|
|
|
|
container_element(container_element const& ce)
|
|
: ptr(ce.ptr.get() == 0 ? 0 : new element_type(*ce.ptr.get()))
|
|
, container(ce.container)
|
|
, index(ce.index)
|
|
{
|
|
}
|
|
|
|
~container_element()
|
|
{
|
|
if (!is_detached())
|
|
get_links().remove(*this);
|
|
}
|
|
|
|
element_type& operator*() const
|
|
{
|
|
if (is_detached())
|
|
return *get_pointer(ptr);
|
|
return Policies::get_item(get_container(), index);
|
|
}
|
|
|
|
element_type* get() const
|
|
{
|
|
if (is_detached())
|
|
return get_pointer(ptr);
|
|
return &Policies::get_item(get_container(), index);
|
|
}
|
|
|
|
void
|
|
detach()
|
|
{
|
|
if (!is_detached())
|
|
{
|
|
ptr.reset(
|
|
new element_type(
|
|
Policies::get_item(get_container(), index)));
|
|
container = object(); // free container. reset it to None
|
|
}
|
|
}
|
|
|
|
bool
|
|
is_detached() const
|
|
{
|
|
return get_pointer(ptr) != 0;
|
|
}
|
|
|
|
Container&
|
|
get_container() const
|
|
{
|
|
return extract<Container&>(container)();
|
|
}
|
|
|
|
Index
|
|
get_index() const
|
|
{
|
|
return index;
|
|
}
|
|
|
|
void
|
|
set_index(Index i)
|
|
{
|
|
index = i;
|
|
}
|
|
|
|
static proxy_links<self_t, Container>&
|
|
get_links()
|
|
{
|
|
// All container_element(s) maintain links to
|
|
// its container in a global map (see proxy_links).
|
|
// This global "links" map is a singleton.
|
|
|
|
static proxy_links<self_t, Container> links;
|
|
return links; // singleton
|
|
}
|
|
|
|
private:
|
|
|
|
container_element& operator=(container_element const& ce);
|
|
|
|
scoped_ptr<element_type> ptr;
|
|
object container;
|
|
Index index;
|
|
};
|
|
|
|
template <
|
|
class Container
|
|
, class DerivedPolicies
|
|
, class ContainerElement
|
|
, class Index
|
|
>
|
|
struct no_proxy_helper
|
|
{
|
|
static void
|
|
register_container_element()
|
|
{
|
|
}
|
|
|
|
template <class DataType>
|
|
static object
|
|
base_get_item_helper(DataType const& p, mpl::true_)
|
|
{
|
|
return object(ptr(p));
|
|
}
|
|
|
|
template <class DataType>
|
|
static object
|
|
base_get_item_helper(DataType const& x, mpl::false_)
|
|
{
|
|
return object(x);
|
|
}
|
|
|
|
static object
|
|
base_get_item_(back_reference<Container&> const& container, PyObject* i)
|
|
{
|
|
return base_get_item_helper(
|
|
DerivedPolicies::get_item(
|
|
container.get(), DerivedPolicies::
|
|
convert_index(container.get(), i))
|
|
, is_pointer<BOOST_DEDUCED_TYPENAME Container::value_type>()
|
|
);
|
|
}
|
|
|
|
static void
|
|
base_replace_indexes(
|
|
Container& /*container*/, Index /*from*/,
|
|
Index /*to*/, Index /*n*/)
|
|
{
|
|
}
|
|
|
|
template <class NoSlice>
|
|
static void
|
|
base_erase_index(
|
|
Container& /*container*/, Index /*i*/, NoSlice /*no_slice*/)
|
|
{
|
|
}
|
|
|
|
static void
|
|
base_erase_indexes(Container& /*container*/, Index /*from*/, Index /*to*/)
|
|
{
|
|
}
|
|
};
|
|
|
|
template <
|
|
class Container
|
|
, class DerivedPolicies
|
|
, class ContainerElement
|
|
, class Index
|
|
>
|
|
struct proxy_helper
|
|
{
|
|
static void
|
|
register_container_element()
|
|
{
|
|
register_ptr_to_python<ContainerElement>();
|
|
}
|
|
|
|
static object
|
|
base_get_item_(back_reference<Container&> const& container, PyObject* i)
|
|
{
|
|
// Proxy
|
|
Index idx = DerivedPolicies::convert_index(container.get(), i);
|
|
|
|
if (PyObject* shared =
|
|
ContainerElement::get_links().find(container.get(), idx))
|
|
{
|
|
handle<> h(python::borrowed(shared));
|
|
return object(h);
|
|
}
|
|
else
|
|
{
|
|
object prox(ContainerElement(container.source(), idx));
|
|
ContainerElement::
|
|
get_links().add(prox.ptr(), container.get());
|
|
return prox;
|
|
}
|
|
}
|
|
|
|
static void
|
|
base_replace_indexes(
|
|
Container& container, Index from,
|
|
Index to, Index n)
|
|
{
|
|
ContainerElement::get_links().replace(container, from, to, n);
|
|
}
|
|
|
|
template <class NoSlice>
|
|
static void
|
|
base_erase_index(
|
|
Container& container, Index i, NoSlice no_slice)
|
|
{
|
|
ContainerElement::get_links().erase(container, i, no_slice);
|
|
}
|
|
|
|
static void
|
|
base_erase_indexes(
|
|
Container& container, Index from, Index to)
|
|
{
|
|
ContainerElement::get_links().erase(container, from, to);
|
|
}
|
|
};
|
|
|
|
template <
|
|
class Container
|
|
, class DerivedPolicies
|
|
, class ProxyHandler
|
|
, class Data
|
|
, class Index
|
|
>
|
|
struct slice_helper
|
|
{
|
|
static object
|
|
base_get_slice(Container& container, PySliceObject* slice)
|
|
{
|
|
Index from, to;
|
|
base_get_slice_data(container, slice, from, to);
|
|
return DerivedPolicies::get_slice(container, from, to);
|
|
}
|
|
|
|
static void
|
|
base_get_slice_data(
|
|
Container& container, PySliceObject* slice, Index& from_, Index& to_)
|
|
{
|
|
if (Py_None != slice->step) {
|
|
PyErr_SetString( PyExc_IndexError, "slice step size not supported.");
|
|
throw_error_already_set();
|
|
}
|
|
|
|
Index min_index = DerivedPolicies::get_min_index(container);
|
|
Index max_index = DerivedPolicies::get_max_index(container);
|
|
|
|
if (Py_None == slice->start) {
|
|
from_ = min_index;
|
|
}
|
|
else {
|
|
long from = extract<long>( slice->start);
|
|
if (from < 0) // Negative slice index
|
|
from += max_index;
|
|
if (from < 0) // Clip lower bounds to zero
|
|
from = 0;
|
|
from_ = boost::numeric_cast<Index>(from);
|
|
if (from_ > max_index) // Clip upper bounds to max_index.
|
|
from_ = max_index;
|
|
}
|
|
|
|
if (Py_None == slice->stop) {
|
|
to_ = max_index;
|
|
}
|
|
else {
|
|
long to = extract<long>( slice->stop);
|
|
if (to < 0)
|
|
to += max_index;
|
|
if (to < 0)
|
|
to = 0;
|
|
to_ = boost::numeric_cast<Index>(to);
|
|
if (to_ > max_index)
|
|
to_ = max_index;
|
|
}
|
|
}
|
|
|
|
static void
|
|
base_set_slice(Container& container, PySliceObject* slice, PyObject* v)
|
|
{
|
|
Index from, to;
|
|
base_get_slice_data(container, slice, from, to);
|
|
|
|
extract<Data&> elem(v);
|
|
// try if elem is an exact Data
|
|
if (elem.check())
|
|
{
|
|
ProxyHandler::base_replace_indexes(container, from, to, 1);
|
|
DerivedPolicies::set_slice(container, from, to, elem());
|
|
}
|
|
else
|
|
{
|
|
// try to convert elem to Data
|
|
extract<Data> elem(v);
|
|
if (elem.check())
|
|
{
|
|
ProxyHandler::base_replace_indexes(container, from, to, 1);
|
|
DerivedPolicies::set_slice(container, from, to, elem());
|
|
}
|
|
else
|
|
{
|
|
// Otherwise, it must be a list or some container
|
|
handle<> l_(python::borrowed(v));
|
|
object l(l_);
|
|
|
|
std::vector<Data> temp;
|
|
for (int i = 0; i < l.attr("__len__")(); i++)
|
|
{
|
|
object elem(l[i]);
|
|
extract<Data const&> x(elem);
|
|
// try if elem is an exact Data type
|
|
if (x.check())
|
|
{
|
|
temp.push_back(x());
|
|
}
|
|
else
|
|
{
|
|
// try to convert elem to Data type
|
|
extract<Data> x(elem);
|
|
if (x.check())
|
|
{
|
|
temp.push_back(x());
|
|
}
|
|
else
|
|
{
|
|
PyErr_SetString(PyExc_TypeError,
|
|
"Invalid sequence element");
|
|
throw_error_already_set();
|
|
}
|
|
}
|
|
}
|
|
|
|
ProxyHandler::base_replace_indexes(container, from, to,
|
|
temp.end()-temp.begin());
|
|
DerivedPolicies::set_slice(container, from, to,
|
|
temp.begin(), temp.end());
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
base_delete_slice(Container& container, PySliceObject* slice)
|
|
{
|
|
Index from, to;
|
|
base_get_slice_data(container, slice, from, to);
|
|
ProxyHandler::base_erase_indexes(container, from, to);
|
|
DerivedPolicies::delete_slice(container, from, to);
|
|
}
|
|
};
|
|
|
|
template <
|
|
class Container
|
|
, class DerivedPolicies
|
|
, class ProxyHandler
|
|
, class Data
|
|
, class Index
|
|
>
|
|
struct no_slice_helper
|
|
{
|
|
static void
|
|
slicing_not_suported()
|
|
{
|
|
PyErr_SetString(PyExc_RuntimeError, "Slicing not supported");
|
|
throw_error_already_set();
|
|
}
|
|
|
|
static object
|
|
base_get_slice(Container& /*container*/, PySliceObject* /*slice*/)
|
|
{
|
|
slicing_not_suported();
|
|
return object();
|
|
}
|
|
|
|
static void
|
|
base_set_slice(Container& /*container*/, PySliceObject* /*slice*/, PyObject* /*v*/)
|
|
{
|
|
slicing_not_suported();
|
|
}
|
|
|
|
static void
|
|
base_delete_slice(Container& /*container*/, PySliceObject* /*slice*/)
|
|
{
|
|
slicing_not_suported();
|
|
}
|
|
};
|
|
|
|
#ifdef BOOST_NO_ARGUMENT_DEPENDENT_LOOKUP
|
|
}} // namespace python::detail
|
|
#endif
|
|
|
|
template <class Container, class Index, class Policies>
|
|
inline typename Policies::data_type*
|
|
get_pointer(
|
|
python::detail::container_element<Container, Index, Policies> const& p)
|
|
{
|
|
// Get the pointer of a container_element smart pointer
|
|
return p.get();
|
|
}
|
|
|
|
#ifndef BOOST_NO_ARGUMENT_DEPENDENT_LOOKUP
|
|
// Don't hide these other get_pointer overloads
|
|
using boost::python::get_pointer;
|
|
using boost::get_pointer;
|
|
}} // namespace python::detail
|
|
#endif
|
|
|
|
} // namespace boost
|
|
|
|
#endif // INDEXING_SUITE_DETAIL_JDG20036_HPP
|