402 lines
11 KiB
C++
402 lines
11 KiB
C++
//
|
|
// immer: immutable data structures for C++
|
|
// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
|
|
//
|
|
// This software is distributed under the Boost Software License, Version 1.0.
|
|
// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
|
|
//
|
|
|
|
extern "C" {
|
|
#include <Python.h>
|
|
#include <structmember.h>
|
|
}
|
|
|
|
#include <immer/vector.hpp>
|
|
#include <immer/algorithm.hpp>
|
|
#include <immer/refcount/unsafe_refcount_policy.hpp>
|
|
|
|
#include <iostream>
|
|
|
|
namespace {
|
|
|
|
struct heap_t
|
|
{
|
|
template <typename ...Tags>
|
|
static void* allocate(std::size_t size, Tags...)
|
|
{
|
|
return PyMem_Malloc(size);
|
|
}
|
|
|
|
template <typename ...Tags>
|
|
static void deallocate(std::size_t, void* obj, Tags...)
|
|
{
|
|
PyMem_Free(obj);
|
|
}
|
|
};
|
|
|
|
struct object_t
|
|
{
|
|
struct wrap_t {};
|
|
struct adopt_t {};
|
|
|
|
PyObject* ptr_ = nullptr;
|
|
|
|
object_t() = delete;
|
|
~object_t() { Py_XDECREF(ptr_); }
|
|
|
|
explicit object_t(PyObject* p, wrap_t) : ptr_{p} {}
|
|
explicit object_t(PyObject* p, adopt_t) : ptr_{p}
|
|
{
|
|
assert(p);
|
|
Py_INCREF(p);
|
|
}
|
|
|
|
static object_t wrap(PyObject* p) { return object_t{p, wrap_t{}}; }
|
|
static object_t adopt(PyObject* p) { return object_t{p, adopt_t{}}; }
|
|
|
|
object_t(const object_t& o) : ptr_(o.ptr_) { Py_INCREF(ptr_); }
|
|
object_t(object_t&& o) { std::swap(ptr_, o.ptr_); }
|
|
|
|
object_t& operator=(const object_t& o)
|
|
{
|
|
Py_XINCREF(o.ptr_);
|
|
Py_XDECREF(ptr_);
|
|
ptr_ = o.ptr_;
|
|
return *this;
|
|
}
|
|
object_t& operator=(object_t&& o)
|
|
{
|
|
std::swap(ptr_, o.ptr_);
|
|
return *this;
|
|
}
|
|
|
|
PyObject* release()
|
|
{
|
|
auto p = ptr_;
|
|
ptr_ = nullptr;
|
|
return p;
|
|
}
|
|
|
|
PyObject* get() const { return ptr_; }
|
|
};
|
|
|
|
using memory_t = immer::memory_policy<
|
|
immer::unsafe_free_list_heap_policy<heap_t>,
|
|
immer::unsafe_refcount_policy>;
|
|
|
|
using vector_impl_t = immer::vector<object_t, memory_t>;
|
|
|
|
struct vector_t
|
|
{
|
|
PyObject_HEAD
|
|
vector_impl_t impl;
|
|
PyObject* in_weakreflist;
|
|
|
|
static PyTypeObject type;
|
|
};
|
|
|
|
vector_t* empty_vector = nullptr;
|
|
|
|
vector_t* make_vector()
|
|
{
|
|
auto* v = PyObject_GC_New(vector_t, &vector_t::type);
|
|
new (&v->impl) vector_impl_t{};
|
|
v->in_weakreflist = nullptr;
|
|
PyObject_GC_Track((PyObject*)v);
|
|
return v;
|
|
}
|
|
|
|
vector_t* make_vector(vector_impl_t&& impl)
|
|
{
|
|
auto v = PyObject_GC_New(vector_t, &vector_t::type);
|
|
new (&v->impl) vector_impl_t{std::move(impl)};
|
|
v->in_weakreflist = nullptr;
|
|
PyObject_GC_Track((PyObject*)v);
|
|
return v;
|
|
}
|
|
|
|
auto todo()
|
|
{
|
|
PyErr_SetString(PyExc_RuntimeError, "immer: todo!");
|
|
return nullptr;
|
|
}
|
|
|
|
void vector_dealloc(vector_t* self)
|
|
{
|
|
if (self->in_weakreflist != nullptr)
|
|
PyObject_ClearWeakRefs((PyObject*)self);
|
|
|
|
PyObject_GC_UnTrack((PyObject*)self);
|
|
Py_TRASHCAN_SAFE_BEGIN(self);
|
|
|
|
self->impl.~vector_impl_t();
|
|
|
|
PyObject_GC_Del(self);
|
|
Py_TRASHCAN_SAFE_END(self);
|
|
}
|
|
|
|
PyObject* vector_to_list(vector_t* self)
|
|
{
|
|
auto list = PyList_New(self->impl.size());
|
|
auto idx = 0;
|
|
immer::for_each(self->impl, [&] (auto&& obj) {
|
|
auto o = obj.get();
|
|
Py_INCREF(o);
|
|
PyList_SET_ITEM(list, idx, o);
|
|
++idx;
|
|
});
|
|
return list;
|
|
}
|
|
|
|
PyObject* vector_repr(vector_t *self)
|
|
{
|
|
auto list = vector_to_list(self);
|
|
auto list_repr = PyObject_Repr(list);
|
|
Py_DECREF(list);
|
|
|
|
if (!list_repr) return nullptr;
|
|
|
|
#if PY_MAJOR_VERSION >= 3
|
|
auto s = PyUnicode_FromFormat("%s%U%s", "immer.vector(", list_repr, ")");
|
|
Py_DECREF(list_repr);
|
|
#else
|
|
auto s = PyString_FromString("immer.vector(");
|
|
PyString_ConcatAndDel(&s, list_repr);
|
|
PyString_ConcatAndDel(&s, PyString_FromString(")"));
|
|
#endif
|
|
return s;
|
|
}
|
|
|
|
Py_ssize_t vector_len(vector_t* self)
|
|
{
|
|
return self->impl.size();
|
|
}
|
|
|
|
PyObject* vector_extend(vector_t* self, PyObject* iterable)
|
|
{
|
|
return todo();
|
|
}
|
|
|
|
vector_t* vector_append(vector_t* self, PyObject *obj)
|
|
{
|
|
assert(obj != nullptr);
|
|
return make_vector(self->impl.push_back(object_t::adopt(obj)));
|
|
}
|
|
|
|
vector_t* vector_set(vector_t* self, PyObject* args)
|
|
{
|
|
PyObject* obj = nullptr;
|
|
Py_ssize_t pos;
|
|
// the n parses for size, the O parses for a Python object
|
|
if(!PyArg_ParseTuple(args, "nO", &pos, &obj)) {
|
|
return nullptr;
|
|
}
|
|
if (pos < 0)
|
|
pos += self->impl.size();
|
|
if (pos < 0 || pos > (Py_ssize_t)self->impl.size()) {
|
|
PyErr_Format(PyExc_IndexError, "Index out of range: %zi", pos);
|
|
return nullptr;
|
|
}
|
|
return make_vector(self->impl.set(pos, object_t::adopt(obj)));
|
|
}
|
|
|
|
PyObject* vector_new(PyTypeObject* subtype, PyObject *args, PyObject *kwds)
|
|
{
|
|
Py_INCREF(empty_vector);
|
|
return (PyObject*)empty_vector;
|
|
}
|
|
|
|
long vector_hash(vector_t* self)
|
|
{
|
|
todo();
|
|
return 0;
|
|
}
|
|
|
|
PyObject* vector_get_item(vector_t* self, Py_ssize_t pos)
|
|
{
|
|
if (pos < 0)
|
|
pos += self->impl.size();
|
|
if (pos < 0 || pos >= (Py_ssize_t)self->impl.size()) {
|
|
PyErr_Format(PyExc_IndexError, "Index out of range: %zi", pos);
|
|
return nullptr;
|
|
}
|
|
auto r = self->impl[pos];
|
|
return r.release();
|
|
}
|
|
|
|
PyObject* vector_subscript(vector_t* self, PyObject* item)
|
|
{
|
|
if (PyIndex_Check(item)) {
|
|
auto i = PyNumber_AsSsize_t(item, PyExc_IndexError);
|
|
if (i == -1 && PyErr_Occurred())
|
|
return nullptr;
|
|
return vector_get_item(self, i);
|
|
} else if (PySlice_Check(item)) {
|
|
return todo();
|
|
} else {
|
|
PyErr_Format(PyExc_TypeError,
|
|
"vector indices must be integers, not %.200s",
|
|
Py_TYPE(item)->tp_name);
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
PyObject* vector_repeat(vector_t* self, Py_ssize_t n)
|
|
{
|
|
return todo();
|
|
}
|
|
|
|
int vector_traverse(vector_t* self, visitproc visit, void* arg)
|
|
{
|
|
auto result = 0;
|
|
immer::all_of(self->impl, [&] (auto&& o) {
|
|
return 0 == (result = [&] {
|
|
Py_VISIT(o.get());
|
|
return 0;
|
|
}());
|
|
});
|
|
return result;
|
|
}
|
|
|
|
PyObject* vector_richcompare(PyObject* v, PyObject* w, int op)
|
|
{
|
|
return todo();
|
|
}
|
|
|
|
PyObject* vector_iter(PyObject* self)
|
|
{
|
|
return todo();
|
|
}
|
|
|
|
PyMethodDef vector_methods[] =
|
|
{
|
|
{"append", (PyCFunction)vector_append, METH_O, "Appends an element"},
|
|
{"set", (PyCFunction)vector_set, METH_VARARGS, "Inserts an element at the specified position"},
|
|
{"extend", (PyCFunction)vector_extend, METH_O|METH_COEXIST, "Extend"},
|
|
{"tolist", (PyCFunction)vector_to_list, METH_NOARGS, "Convert to list"},
|
|
{0}
|
|
};
|
|
|
|
PyMemberDef vector_members[] =
|
|
{
|
|
{0} /* sentinel */
|
|
};
|
|
|
|
PySequenceMethods vector_sequence_methods =
|
|
{
|
|
(lenfunc)vector_len, /* sq_length */
|
|
(binaryfunc)vector_extend, /* sq_concat */
|
|
(ssizeargfunc)vector_repeat, /* sq_repeat */
|
|
(ssizeargfunc)vector_get_item, /* sq_item */
|
|
0, /* sq_slice */
|
|
0, /* sq_ass_item */
|
|
0, /* sq_ass_slice */
|
|
0, /* sq_contains */
|
|
0, /* sq_inplace_concat */
|
|
0, /* sq_inplace_repeat */
|
|
};
|
|
|
|
PyMappingMethods vector_mapping_methods =
|
|
{
|
|
(lenfunc)vector_len,
|
|
(binaryfunc)vector_subscript,
|
|
0
|
|
};
|
|
|
|
PyTypeObject vector_t::type =
|
|
{
|
|
PyVarObject_HEAD_INIT(NULL, 0)
|
|
"immer.Vector", /* tp_name */
|
|
sizeof(vector_t), /* tp_basicsize */
|
|
0, /* tp_itemsize */
|
|
(destructor)vector_dealloc, /* tp_dealloc */
|
|
0, /* tp_print */
|
|
0, /* tp_getattr */
|
|
0, /* tp_setattr */
|
|
0, /* tp_compare */
|
|
(reprfunc)vector_repr, /* tp_repr */
|
|
0, /* tp_as_number */
|
|
&vector_sequence_methods, /* tp_as_sequence */
|
|
&vector_mapping_methods, /* tp_as_mapping */
|
|
(hashfunc)vector_hash, /* tp_hash */
|
|
0, /* tp_call */
|
|
0, /* tp_str */
|
|
0, /* tp_getattro */
|
|
0, /* tp_setattro */
|
|
0, /* tp_as_buffer */
|
|
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, /* tp_flags */
|
|
"", /* tp_doc */
|
|
(traverseproc)vector_traverse, /* tp_traverse */
|
|
0, /* tp_clear */
|
|
vector_richcompare, /* tp_richcompare */
|
|
offsetof(vector_t, in_weakreflist), /* tp_weaklistoffset */
|
|
vector_iter, /* tp_iter */
|
|
0, /* tp_iternext */
|
|
vector_methods, /* tp_methods */
|
|
vector_members, /* tp_members */
|
|
0, /* tp_getset */
|
|
0, /* tp_base */
|
|
0, /* tp_dict */
|
|
0, /* tp_descr_get */
|
|
0, /* tp_descr_set */
|
|
0, /* tp_dictoffset */
|
|
0, /* tp_init */
|
|
0, /* tp_alloc */
|
|
vector_new, /* tp_new */
|
|
};
|
|
|
|
#if PY_MAJOR_VERSION >= 3
|
|
struct PyModuleDef module_def =
|
|
{
|
|
PyModuleDef_HEAD_INIT,
|
|
"immer_python_module", /* m_name */
|
|
"", /* m_doc */
|
|
-1, /* m_size */
|
|
/module_methods, /* m_methods */
|
|
0, /* m_reload */
|
|
0, /* m_traverse */
|
|
0, /* m_clear */
|
|
0, /* m_free */
|
|
};
|
|
#endif
|
|
|
|
PyMethodDef module_methods[] = {
|
|
{0, 0, 0, 0}
|
|
};
|
|
|
|
PyObject* module_init()
|
|
{
|
|
if (PyType_Ready(&vector_t::type) < 0)
|
|
return nullptr;
|
|
|
|
#if PY_MAJOR_VERSION >= 3
|
|
auto m = PyModule_Create(&module_def);
|
|
#else
|
|
auto m = Py_InitModule3("immer_python_module", module_methods, "");
|
|
#endif
|
|
if (!m)
|
|
return nullptr;
|
|
|
|
if (!empty_vector)
|
|
empty_vector = make_vector();
|
|
|
|
Py_INCREF(&vector_t::type);
|
|
PyModule_AddObject(m, "Vector", (PyObject*) &vector_t::type);
|
|
return m;
|
|
}
|
|
|
|
} // anonymous namespace
|
|
|
|
extern "C" {
|
|
#if PY_MAJOR_VERSION >= 3
|
|
PyMODINIT_FUNC PyInit_immer_python_module()
|
|
{
|
|
return module_init();
|
|
}
|
|
#else
|
|
PyMODINIT_FUNC initimmer_python_module()
|
|
{
|
|
module_init();
|
|
}
|
|
#endif
|
|
}
|