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 "pygi-type.h"
26 #include "pygi-basictype.h"
27 #include "pygi-util.h"
28 
29 #include <girepository.h>
30 
31 struct _PyGIBoxed {
32     PyGBoxed base;
33     gboolean slice_allocated;
34     gsize size;
35 };
36 
37 static void
boxed_clear(PyGIBoxed * self)38 boxed_clear (PyGIBoxed *self)
39 {
40     gpointer boxed = pyg_boxed_get_ptr (self);
41     GType g_type = ((PyGBoxed *)self)->gtype;
42 
43     if ( ( (PyGBoxed *) self)->free_on_dealloc && boxed != NULL) {
44         if (self->slice_allocated) {
45             if (g_type && g_type_is_a (g_type, G_TYPE_VALUE))
46                 g_value_unset (boxed);
47             g_slice_free1 (self->size, boxed);
48             self->slice_allocated = FALSE;
49             self->size = 0;
50         } else {
51             g_boxed_free (g_type, boxed);
52         }
53     }
54     pyg_boxed_set_ptr (self, NULL);
55 }
56 
57 static PyObject *
boxed_clear_wrapper(PyGIBoxed * self)58 boxed_clear_wrapper (PyGIBoxed *self)
59 {
60     boxed_clear (self);
61 
62     Py_RETURN_NONE;
63 }
64 
65 
66 static void
boxed_dealloc(PyGIBoxed * self)67 boxed_dealloc (PyGIBoxed *self)
68 {
69     boxed_clear (self);
70 
71     Py_TYPE (self)->tp_free ((PyObject *)self);
72 }
73 
74 void *
pygi_boxed_alloc(GIBaseInfo * info,gsize * size_out)75 pygi_boxed_alloc (GIBaseInfo *info, gsize *size_out)
76 {
77     gpointer boxed = NULL;
78     gsize size = 0;
79 
80     switch (g_base_info_get_type (info)) {
81         case GI_INFO_TYPE_UNION:
82             size = g_union_info_get_size ( (GIUnionInfo *) info);
83             break;
84         case GI_INFO_TYPE_BOXED:
85         case GI_INFO_TYPE_STRUCT:
86             size = g_struct_info_get_size ( (GIStructInfo *) info);
87             break;
88         default:
89             PyErr_Format (PyExc_TypeError,
90                           "info should be Boxed or Union, not '%d'",
91                           g_base_info_get_type (info));
92             return NULL;
93     }
94 
95     if (size == 0) {
96         PyErr_Format (PyExc_TypeError,
97             "boxed cannot be created directly; try using a constructor, see: help(%s.%s)",
98             g_base_info_get_namespace (info),
99             g_base_info_get_name (info));
100         return NULL;
101     }
102 
103     if( size_out != NULL)
104         *size_out = size;
105 
106     boxed = g_slice_alloc0 (size);
107     if (boxed == NULL)
108         PyErr_NoMemory();
109     return boxed;
110 }
111 
112 static PyObject *
boxed_new(PyTypeObject * type,PyObject * args,PyObject * kwargs)113 boxed_new (PyTypeObject *type,
114             PyObject     *args,
115             PyObject     *kwargs)
116 {
117     GIBaseInfo *info;
118     gsize size = 0;
119     gpointer boxed;
120     PyGIBoxed *self = NULL;
121 
122     info = _pygi_object_get_gi_info ( (PyObject *) type, &PyGIBaseInfo_Type);
123     if (info == NULL) {
124         if (PyErr_ExceptionMatches (PyExc_AttributeError)) {
125             PyErr_Format (PyExc_TypeError, "missing introspection information");
126         }
127         return NULL;
128     }
129 
130     boxed = pygi_boxed_alloc (info, &size);
131     if (boxed == NULL) {
132         goto out;
133     }
134 
135     self = (PyGIBoxed *) pygi_boxed_new (type, boxed, TRUE, size);
136     if (self == NULL) {
137         g_slice_free1 (size, boxed);
138         goto out;
139     }
140 
141     self->size = size;
142     self->slice_allocated = TRUE;
143 
144 out:
145     g_base_info_unref (info);
146 
147     return (PyObject *) self;
148 }
149 
150 static int
boxed_init(PyObject * self,PyObject * args,PyObject * kwargs)151 boxed_init (PyObject *self,
152              PyObject *args,
153              PyObject *kwargs)
154 {
155     static char *kwlist[] = { NULL };
156 
157     if (!PyArg_ParseTupleAndKeywords (args, kwargs, "", kwlist)) {
158         PyErr_Clear ();
159         PyErr_Warn (PyExc_DeprecationWarning,
160                 "Passing arguments to gi.types.Boxed.__init__() is deprecated. "
161                 "All arguments passed will be ignored.");
162     }
163 
164     /* Don't call PyGBoxed's init, which raises an exception. */
165     return 0;
166 }
167 
168 PYGI_DEFINE_TYPE("gi.Boxed", PyGIBoxed_Type, PyGIBoxed);
169 
170 PyObject *
pygi_boxed_new(PyTypeObject * type,gpointer boxed,gboolean free_on_dealloc,gsize allocated_slice)171 pygi_boxed_new (PyTypeObject *type,
172                 gpointer      boxed,
173                 gboolean      free_on_dealloc,
174                 gsize         allocated_slice)
175 {
176     PyGIBoxed *self;
177 
178     if (!boxed) {
179         Py_RETURN_NONE;
180     }
181 
182     if (!PyType_IsSubtype (type, &PyGIBoxed_Type)) {
183         PyErr_SetString (PyExc_TypeError, "must be a subtype of gi.Boxed");
184         return NULL;
185     }
186 
187     self = (PyGIBoxed *) type->tp_alloc (type, 0);
188     if (self == NULL) {
189         return NULL;
190     }
191 
192     ( (PyGBoxed *) self)->gtype = pyg_type_from_object ( (PyObject *) type);
193     ( (PyGBoxed *) self)->free_on_dealloc = free_on_dealloc;
194     pyg_boxed_set_ptr (self, boxed);
195     if (allocated_slice > 0) {
196         self->size = allocated_slice;
197         self->slice_allocated = TRUE;
198     } else {
199         self->size = 0;
200         self->slice_allocated = FALSE;
201     }
202 
203     return (PyObject *) self;
204 }
205 
206 /**
207  * pygi_boxed_copy_in_place:
208  *
209  * Replace the boxed pointer held by this wrapper with a boxed copy
210  * freeing the previously held pointer (when free_on_dealloc is TRUE).
211  * This can be used in cases where Python is passed a reference which
212  * it does not own and the wrapper is held by the Python program
213  * longer than the duration of a callback it was passed to.
214  */
215 void
pygi_boxed_copy_in_place(PyGIBoxed * self)216 pygi_boxed_copy_in_place (PyGIBoxed *self)
217 {
218     PyGBoxed *pygboxed = (PyGBoxed *)self;
219     gpointer ptr = pyg_boxed_get_ptr (self);
220     gpointer copy = NULL;
221 
222     if (ptr)
223         copy = g_boxed_copy (pygboxed->gtype, ptr);
224 
225     boxed_clear (self);
226     pyg_boxed_set_ptr (pygboxed, copy);
227     pygboxed->free_on_dealloc = TRUE;
228 }
229 
230 static PyMethodDef boxed_methods[] = {
231     { "_clear_boxed", (PyCFunction)boxed_clear_wrapper, METH_NOARGS },
232     { NULL, NULL, 0 }
233 };
234 
235 /**
236  * Returns 0 on success, or -1 and sets an exception.
237  */
238 int
pygi_boxed_register_types(PyObject * m)239 pygi_boxed_register_types (PyObject *m)
240 {
241     Py_TYPE(&PyGIBoxed_Type) = &PyType_Type;
242     g_assert (Py_TYPE (&PyGBoxed_Type) != NULL);
243     PyGIBoxed_Type.tp_base = &PyGBoxed_Type;
244     PyGIBoxed_Type.tp_new = (newfunc) boxed_new;
245     PyGIBoxed_Type.tp_init = (initproc) boxed_init;
246     PyGIBoxed_Type.tp_dealloc = (destructor) boxed_dealloc;
247     PyGIBoxed_Type.tp_flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE);
248     PyGIBoxed_Type.tp_methods = boxed_methods;
249 
250     if (PyType_Ready (&PyGIBoxed_Type) < 0)
251         return -1;
252     Py_INCREF ((PyObject *) &PyGIBoxed_Type);
253     if (PyModule_AddObject (m, "Boxed", (PyObject *) &PyGIBoxed_Type) < 0) {
254         Py_DECREF ((PyObject *) &PyGIBoxed_Type);
255         return -1;
256     }
257 
258     return 0;
259 }
260