Imported Upstream version 3.21.91
[platform/upstream/python-gobject.git] / gi / pygi-boxed.c
1 /* -*- Mode: C; c-basic-offset: 4 -*-
2  * vim: tabstop=4 shiftwidth=4 expandtab
3  *
4  * Copyright (C) 2009 Simon van der Linden <svdlinden@src.gnome.org>
5  *
6  *   pygi-boxed.c: wrapper to handle registered structures.
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public
10  * License as published by the Free Software Foundation; either
11  * version 2.1 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library; if not, see <http://www.gnu.org/licenses/>.
20  */
21
22 #include "pygi-boxed.h"
23 #include "pygi-info.h"
24 #include "pygboxed.h"
25 #include "pygtype.h"
26
27 #include <girepository.h>
28 #include <pyglib-python-compat.h>
29
30 static void
31 _boxed_dealloc (PyGIBoxed *self)
32 {
33     Py_TYPE (self)->tp_free ((PyObject *)self);
34 }
35
36 static PyObject *
37 boxed_del (PyGIBoxed *self)
38 {
39     GType g_type;
40     gpointer boxed = pyg_boxed_get_ptr (self);
41
42     if ( ( (PyGBoxed *) self)->free_on_dealloc && boxed != NULL) {
43         if (self->slice_allocated) {
44             g_slice_free1 (self->size, boxed);
45         } else {
46             g_type = pyg_type_from_object ( (PyObject *) self);
47             g_boxed_free (g_type, boxed);
48         }
49     }
50     pyg_boxed_set_ptr (self, NULL);
51
52     Py_RETURN_NONE;
53 }
54
55 void *
56 _pygi_boxed_alloc (GIBaseInfo *info, gsize *size_out)
57 {
58     gpointer boxed = NULL;
59     gsize size = 0;
60
61     switch (g_base_info_get_type (info)) {
62         case GI_INFO_TYPE_UNION:
63             size = g_union_info_get_size ( (GIUnionInfo *) info);
64             break;
65         case GI_INFO_TYPE_BOXED:
66         case GI_INFO_TYPE_STRUCT:
67             size = g_struct_info_get_size ( (GIStructInfo *) info);
68             break;
69         default:
70             PyErr_Format (PyExc_TypeError,
71                           "info should be Boxed or Union, not '%d'",
72                           g_base_info_get_type (info));
73             return NULL;
74     }
75
76     if (size == 0) {
77         PyErr_Format (PyExc_TypeError,
78             "boxed cannot be created directly; try using a constructor, see: help(%s.%s)",
79             g_base_info_get_namespace (info),
80             g_base_info_get_name (info));
81         return NULL;
82     }
83
84     if( size_out != NULL)
85         *size_out = size;
86
87     boxed = g_slice_alloc0 (size);
88     if (boxed == NULL)
89         PyErr_NoMemory();
90     return boxed;
91 }
92
93 static PyObject *
94 _boxed_new (PyTypeObject *type,
95             PyObject     *args,
96             PyObject     *kwargs)
97 {
98     GIBaseInfo *info;
99     gsize size = 0;
100     gpointer boxed;
101     PyGIBoxed *self = NULL;
102
103     info = _pygi_object_get_gi_info ( (PyObject *) type, &PyGIBaseInfo_Type);
104     if (info == NULL) {
105         if (PyErr_ExceptionMatches (PyExc_AttributeError)) {
106             PyErr_Format (PyExc_TypeError, "missing introspection information");
107         }
108         return NULL;
109     }
110
111     boxed = _pygi_boxed_alloc (info, &size);
112     if (boxed == NULL) {
113         goto out;
114     }
115
116     self = (PyGIBoxed *) _pygi_boxed_new (type, boxed, FALSE, size);
117     if (self == NULL) {
118         g_slice_free1 (size, boxed);
119         goto out;
120     }
121
122     self->size = size;
123     self->slice_allocated = TRUE;
124
125 out:
126     g_base_info_unref (info);
127
128     return (PyObject *) self;
129 }
130
131 static int
132 _boxed_init (PyObject *self,
133              PyObject *args,
134              PyObject *kwargs)
135 {
136     static char *kwlist[] = { NULL };
137
138     if (!PyArg_ParseTupleAndKeywords (args, kwargs, "", kwlist)) {
139         PyErr_Clear ();
140         PyErr_Warn (PyExc_TypeError,
141                 "Passing arguments to gi.types.Boxed.__init__() is deprecated. "
142                 "All arguments passed will be ignored.");
143     }
144
145     /* Don't call PyGBoxed's init, which raises an exception. */
146     return 0;
147 }
148
149 PYGLIB_DEFINE_TYPE("gi.Boxed", PyGIBoxed_Type, PyGIBoxed);
150
151 PyObject *
152 _pygi_boxed_new (PyTypeObject *pytype,
153                  gpointer      boxed,
154                  gboolean      copy_boxed,
155                  gsize         allocated_slice)
156 {
157     PyGIBoxed *self;
158     GType gtype;
159
160     if (!boxed) {
161         Py_RETURN_NONE;
162     }
163
164     if (!PyType_IsSubtype (pytype, &PyGIBoxed_Type)) {
165         PyErr_SetString (PyExc_TypeError, "must be a subtype of gi.Boxed");
166         return NULL;
167     }
168
169     gtype = pyg_type_from_object ((PyObject *)pytype);
170
171     /* Boxed objects with slice allocation means they come from caller allocated
172      * out arguments. In this case copy_boxed does not make sense because we
173      * already own the slice allocated memory and we should be receiving full
174      * ownership transfer. */
175     if (copy_boxed) {
176         g_assert (allocated_slice == 0);
177         boxed = g_boxed_copy (gtype, boxed);
178     }
179
180     self = (PyGIBoxed *) pytype->tp_alloc (pytype, 0);
181     if (self == NULL) {
182         return NULL;
183     }
184
185     /* We always free on dealloc because we always own the memory due to:
186      *   1) copy_boxed == TRUE
187      *   2) allocated_slice > 0
188      *   3) otherwise the mode is assumed "transfer everything".
189      */
190     ((PyGBoxed *)self)->free_on_dealloc = TRUE;
191     ((PyGBoxed *)self)->gtype = gtype;
192     pyg_boxed_set_ptr (self, boxed);
193
194     if (allocated_slice > 0) {
195         self->size = allocated_slice;
196         self->slice_allocated = TRUE;
197     } else {
198         self->size = 0;
199         self->slice_allocated = FALSE;
200     }
201
202     return (PyObject *) self;
203 }
204
205 static PyObject *
206 _pygi_boxed_get_free_on_dealloc(PyGIBoxed *self, void *closure)
207 {
208   return PyBool_FromLong( ((PyGBoxed *)self)->free_on_dealloc );
209 }
210
211 /**
212  * _pygi_boxed_copy_in_place:
213  *
214  * Replace the boxed pointer held by this wrapper with a boxed copy
215  * freeing the previously held pointer (when free_on_dealloc is TRUE).
216  * This can be used in cases where Python is passed a reference which
217  * it does not own and the wrapper is held by the Python program
218  * longer than the duration of a callback it was passed to.
219  */
220 void
221 _pygi_boxed_copy_in_place (PyGIBoxed *self)
222 {
223     PyGBoxed *pygboxed = (PyGBoxed *)self;
224     gpointer copy = g_boxed_copy (pygboxed->gtype, pyg_boxed_get_ptr (self));
225
226     boxed_del (self);
227     pyg_boxed_set_ptr (pygboxed, copy);
228     pygboxed->free_on_dealloc = TRUE;
229 }
230
231 static PyGetSetDef pygi_boxed_getsets[] = {
232     { "_free_on_dealloc", (getter)_pygi_boxed_get_free_on_dealloc, (setter)0 },
233     { NULL, 0, 0 }
234 };
235
236 static PyMethodDef boxed_methods[] = {
237     { "__del__", (PyCFunction)boxed_del, METH_NOARGS },
238     { NULL, NULL, 0 }
239 };
240
241 void
242 _pygi_boxed_register_types (PyObject *m)
243 {
244     Py_TYPE(&PyGIBoxed_Type) = &PyType_Type;
245     PyGIBoxed_Type.tp_base = &PyGBoxed_Type;
246     PyGIBoxed_Type.tp_new = (newfunc) _boxed_new;
247     PyGIBoxed_Type.tp_init = (initproc) _boxed_init;
248     PyGIBoxed_Type.tp_dealloc = (destructor) _boxed_dealloc;
249     PyGIBoxed_Type.tp_flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE);
250     PyGIBoxed_Type.tp_getset = pygi_boxed_getsets;
251     PyGIBoxed_Type.tp_methods = boxed_methods;
252
253     if (PyType_Ready (&PyGIBoxed_Type))
254         return;
255     if (PyModule_AddObject (m, "Boxed", (PyObject *) &PyGIBoxed_Type))
256         return;
257 }