1//
2// immer: immutable data structures for C++
3// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
4//
5// This software is distributed under the Boost Software License, Version 1.0.
6// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
7//
8
9extern "C" {
10#include <Python.h>
11#include <structmember.h>
12}
13
14#include <immer/vector.hpp>
15#include <immer/algorithm.hpp>
16#include <immer/refcount/unsafe_refcount_policy.hpp>
17
18#include <iostream>
19
20namespace {
21
22struct heap_t
23{
24 template <typename ...Tags>
25 static void* allocate(std::size_t size, Tags...)
26 {
27 return PyMem_Malloc(size);
28 }
29
30 template <typename ...Tags>
31 static void deallocate(std::size_t, void* obj, Tags...)
32 {
33 PyMem_Free(obj);
34 }
35};
36
37struct object_t
38{
39 struct wrap_t {};
40 struct adopt_t {};
41
42 PyObject* ptr_ = nullptr;
43
44 object_t() = delete;
45 ~object_t() { Py_XDECREF(ptr_); }
46
47 explicit object_t(PyObject* p, wrap_t) : ptr_{p} {}
48 explicit object_t(PyObject* p, adopt_t) : ptr_{p}
49 {
50 assert(p);
51 Py_INCREF(p);
52 }
53
54 static object_t wrap(PyObject* p) { return object_t{p, wrap_t{}}; }
55 static object_t adopt(PyObject* p) { return object_t{p, adopt_t{}}; }
56
57 object_t(const object_t& o) : ptr_(o.ptr_) { Py_INCREF(ptr_); }
58 object_t(object_t&& o) { std::swap(ptr_, o.ptr_); }
59
60 object_t& operator=(const object_t& o)
61 {
62 Py_XINCREF(o.ptr_);
63 Py_XDECREF(ptr_);
64 ptr_ = o.ptr_;
65 return *this;
66 }
67 object_t& operator=(object_t&& o)
68 {
69 std::swap(ptr_, o.ptr_);
70 return *this;
71 }
72
73 PyObject* release()
74 {
75 auto p = ptr_;
76 ptr_ = nullptr;
77 return p;
78 }
79
80 PyObject* get() const { return ptr_; }
81};
82
83using memory_t = immer::memory_policy<
84 immer::unsafe_free_list_heap_policy<heap_t>,
85 immer::unsafe_refcount_policy>;
86
87using vector_impl_t = immer::vector<object_t, memory_t>;
88
89struct vector_t
90{
91 PyObject_HEAD
92 vector_impl_t impl;
93 PyObject* in_weakreflist;
94
95 static PyTypeObject type;
96};
97
98vector_t* empty_vector = nullptr;
99
100vector_t* make_vector()
101{
102 auto* v = PyObject_GC_New(vector_t, &vector_t::type);
103 new (&v->impl) vector_impl_t{};
104 v->in_weakreflist = nullptr;
105 PyObject_GC_Track((PyObject*)v);
106 return v;
107}
108
109vector_t* make_vector(vector_impl_t&& impl)
110{
111 auto v = PyObject_GC_New(vector_t, &vector_t::type);
112 new (&v->impl) vector_impl_t{std::move(impl)};
113 v->in_weakreflist = nullptr;
114 PyObject_GC_Track((PyObject*)v);
115 return v;
116}
117
118auto todo()
119{
120 PyErr_SetString(PyExc_RuntimeError, "immer: todo!");
121 return nullptr;
122}
123
124void vector_dealloc(vector_t* self)
125{
126 if (self->in_weakreflist != nullptr)
127 PyObject_ClearWeakRefs((PyObject*)self);
128
129 PyObject_GC_UnTrack((PyObject*)self);
130 Py_TRASHCAN_SAFE_BEGIN(self);
131
132 self->impl.~vector_impl_t();
133
134 PyObject_GC_Del(self);
135 Py_TRASHCAN_SAFE_END(self);
136}
137
138PyObject* vector_to_list(vector_t* self)
139{
140 auto list = PyList_New(self->impl.size());
141 auto idx = 0;
142 immer::for_each(self->impl, [&] (auto&& obj) {
143 auto o = obj.get();
144 Py_INCREF(o);
145 PyList_SET_ITEM(list, idx, o);
146 ++idx;
147 });
148 return list;
149}
150
151PyObject* vector_repr(vector_t *self)
152{
153 auto list = vector_to_list(self);
154 auto list_repr = PyObject_Repr(list);
155 Py_DECREF(list);
156
157 if (!list_repr) return nullptr;
158
159#if PY_MAJOR_VERSION >= 3
160 auto s = PyUnicode_FromFormat("%s%U%s", "immer.vector(", list_repr, ")");
161 Py_DECREF(list_repr);
162#else
163 auto s = PyString_FromString("immer.vector(");
164 PyString_ConcatAndDel(&s, list_repr);
165 PyString_ConcatAndDel(&s, PyString_FromString(")"));
166#endif
167 return s;
168}
169
170Py_ssize_t vector_len(vector_t* self)
171{
172 return self->impl.size();
173}
174
175PyObject* vector_extend(vector_t* self, PyObject* iterable)
176{
177 return todo();
178}
179
180vector_t* vector_append(vector_t* self, PyObject *obj)
181{
182 assert(obj != nullptr);
183 return make_vector(self->impl.push_back(object_t::adopt(obj)));
184}
185
186vector_t* vector_set(vector_t* self, PyObject* args)
187{
188 PyObject* obj = nullptr;
189 Py_ssize_t pos;
190 // the n parses for size, the O parses for a Python object
191 if(!PyArg_ParseTuple(args, "nO", &pos, &obj)) {
192 return nullptr;
193 }
194 if (pos < 0)
195 pos += self->impl.size();
196 if (pos < 0 || pos > (Py_ssize_t)self->impl.size()) {
197 PyErr_Format(PyExc_IndexError, "Index out of range: %zi", pos);
198 return nullptr;
199 }
200 return make_vector(self->impl.set(pos, object_t::adopt(obj)));
201}
202
203PyObject* vector_new(PyTypeObject* subtype, PyObject *args, PyObject *kwds)
204{
205 Py_INCREF(empty_vector);
206 return (PyObject*)empty_vector;
207}
208
209long vector_hash(vector_t* self)
210{
211 todo();
212 return 0;
213}
214
215PyObject* vector_get_item(vector_t* self, Py_ssize_t pos)
216{
217 if (pos < 0)
218 pos += self->impl.size();
219 if (pos < 0 || pos >= (Py_ssize_t)self->impl.size()) {
220 PyErr_Format(PyExc_IndexError, "Index out of range: %zi", pos);
221 return nullptr;
222 }
223 auto r = self->impl[pos];
224 return r.release();
225}
226
227PyObject* vector_subscript(vector_t* self, PyObject* item)
228{
229 if (PyIndex_Check(item)) {
230 auto i = PyNumber_AsSsize_t(item, PyExc_IndexError);
231 if (i == -1 && PyErr_Occurred())
232 return nullptr;
233 return vector_get_item(self, i);
234 } else if (PySlice_Check(item)) {
235 return todo();
236 } else {
237 PyErr_Format(PyExc_TypeError,
238 "vector indices must be integers, not %.200s",
239 Py_TYPE(item)->tp_name);
240 return nullptr;
241 }
242}
243
244PyObject* vector_repeat(vector_t* self, Py_ssize_t n)
245{
246 return todo();
247}
248
249int vector_traverse(vector_t* self, visitproc visit, void* arg)
250{
251 auto result = 0;
252 immer::all_of(self->impl, [&] (auto&& o) {
253 return 0 == (result = [&] {
254 Py_VISIT(o.get());
255 return 0;
256 }());
257 });
258 return result;
259}
260
261PyObject* vector_richcompare(PyObject* v, PyObject* w, int op)
262{
263 return todo();
264}
265
266PyObject* vector_iter(PyObject* self)
267{
268 return todo();
269}
270
271PyMethodDef vector_methods[] =
272{
273 {"append", (PyCFunction)vector_append, METH_O, "Appends an element"},
274 {"set", (PyCFunction)vector_set, METH_VARARGS, "Inserts an element at the specified position"},
275 {"extend", (PyCFunction)vector_extend, METH_O|METH_COEXIST, "Extend"},
276 {"tolist", (PyCFunction)vector_to_list, METH_NOARGS, "Convert to list"},
277 {0}
278};
279
280PyMemberDef vector_members[] =
281{
282 {0} /* sentinel */
283};
284
285PySequenceMethods vector_sequence_methods =
286{
287 (lenfunc)vector_len, /* sq_length */
288 (binaryfunc)vector_extend, /* sq_concat */
289 (ssizeargfunc)vector_repeat, /* sq_repeat */
290 (ssizeargfunc)vector_get_item, /* sq_item */
291 0, /* sq_slice */
292 0, /* sq_ass_item */
293 0, /* sq_ass_slice */
294 0, /* sq_contains */
295 0, /* sq_inplace_concat */
296 0, /* sq_inplace_repeat */
297};
298
299PyMappingMethods vector_mapping_methods =
300{
301 (lenfunc)vector_len,
302 (binaryfunc)vector_subscript,
303 0
304};
305
306PyTypeObject vector_t::type =
307{
308 PyVarObject_HEAD_INIT(NULL, 0)
309 "immer.Vector", /* tp_name */
310 sizeof(vector_t), /* tp_basicsize */
311 0, /* tp_itemsize */
312 (destructor)vector_dealloc, /* tp_dealloc */
313 0, /* tp_print */
314 0, /* tp_getattr */
315 0, /* tp_setattr */
316 0, /* tp_compare */
317 (reprfunc)vector_repr, /* tp_repr */
318 0, /* tp_as_number */
319 &vector_sequence_methods, /* tp_as_sequence */
320 &vector_mapping_methods, /* tp_as_mapping */
321 (hashfunc)vector_hash, /* tp_hash */
322 0, /* tp_call */
323 0, /* tp_str */
324 0, /* tp_getattro */
325 0, /* tp_setattro */
326 0, /* tp_as_buffer */
327 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, /* tp_flags */
328 "", /* tp_doc */
329 (traverseproc)vector_traverse, /* tp_traverse */
330 0, /* tp_clear */
331 vector_richcompare, /* tp_richcompare */
332 offsetof(vector_t, in_weakreflist), /* tp_weaklistoffset */
333 vector_iter, /* tp_iter */
334 0, /* tp_iternext */
335 vector_methods, /* tp_methods */
336 vector_members, /* tp_members */
337 0, /* tp_getset */
338 0, /* tp_base */
339 0, /* tp_dict */
340 0, /* tp_descr_get */
341 0, /* tp_descr_set */
342 0, /* tp_dictoffset */
343 0, /* tp_init */
344 0, /* tp_alloc */
345 vector_new, /* tp_new */
346};
347
348#if PY_MAJOR_VERSION >= 3
349struct PyModuleDef module_def =
350{
351 PyModuleDef_HEAD_INIT,
352 "immer_python_module", /* m_name */
353 "", /* m_doc */
354 -1, /* m_size */
355 /module_methods, /* m_methods */
356 0, /* m_reload */
357 0, /* m_traverse */
358 0, /* m_clear */
359 0, /* m_free */
360};
361#endif
362
363PyMethodDef module_methods[] = {
364 {0, 0, 0, 0}
365};
366
367PyObject* module_init()
368{
369 if (PyType_Ready(&vector_t::type) < 0)
370 return nullptr;
371
372#if PY_MAJOR_VERSION >= 3
373 auto m = PyModule_Create(&module_def);
374#else
375 auto m = Py_InitModule3("immer_python_module", module_methods, "");
376#endif
377 if (!m)
378 return nullptr;
379
380 if (!empty_vector)
381 empty_vector = make_vector();
382
383 Py_INCREF(&vector_t::type);
384 PyModule_AddObject(m, "Vector", (PyObject*) &vector_t::type);
385 return m;
386}
387
388} // anonymous namespace
389
390extern "C" {
391#if PY_MAJOR_VERSION >= 3
392PyMODINIT_FUNC PyInit_immer_python_module()
393{
394 return module_init();
395}
396#else
397PyMODINIT_FUNC initimmer_python_module()
398{
399 module_init();
400}
401#endif
402}
403