ROOT  6.06/09
Reference Guide
TPyBufferFactory.cxx
Go to the documentation of this file.
1 // @(#)root/pyroot:$Id$
2 // Author: Wim Lavrijsen, Apr 2004
3 
4 // Bindings
5 #include "PyROOT.h"
6 #include "TPyBufferFactory.h"
7 
8 // Standard
9 #include <map>
10 
11 #if PY_VERSION_HEX >= 0x03000000
12 static PyObject* PyBuffer_FromReadWriteMemory( void* ptr, int size ) {
13  Py_buffer bufinfo = { ptr, NULL, size, 1, 0, 1, NULL, NULL, NULL, NULL,
14 #if PY_VERSION_HEX < 0x03030000
15  { 0, 0 },
16 #endif
17  NULL };
18  return PyMemoryView_FromBuffer( &bufinfo );
19 }
20 #endif
21 
22 
23 //- data ---------------------------------------------------------------------
24 namespace {
25 
26 // top of buffer (rest of buffer object has changed across python versions)
27  struct PyBufferTop_t {
28  PyObject_HEAD
29  PyObject* fBase; // b_base in python
30  void* fPtr; // b_ptr in python
31  Py_ssize_t fSize; // b_size in python
32  };
33 
34 // callable cache
35  std::map< PyObject*, PyObject* > gSizeCallbacks;
36 
37 // create buffer types and copy methods to be adjusted
38 #define PYROOT_PREPARE_PYBUFFER_TYPE( name ) \
39  PyTypeObject Py##name##Buffer_Type; \
40  PySequenceMethods Py##name##Buffer_SeqMethods = *(PyBuffer_Type.tp_as_sequence);\
41  PyMappingMethods Py##name##Buffer_MapMethods;
42 
52 
53 // implement get, str, and length functions
54  Py_ssize_t buffer_length( PyObject* self )
55  {
56  // Retrieve the (type-strided) size of the the buffer; may be a guess.
57 #if PY_VERSION_HEX < 0x03000000
58  Py_ssize_t nlen = (*(PyBuffer_Type.tp_as_sequence->sq_length))(self);
59 #else
60  Py_buffer bufinfo;
61  (*(Py_TYPE(self)->tp_as_buffer->bf_getbuffer))( self, &bufinfo, PyBUF_SIMPLE );
62  Py_ssize_t nlen = bufinfo.len;
63 #endif
64  if ( nlen != INT_MAX ) // INT_MAX is the default, i.e. unknown actual length
65  return nlen;
66 
67  std::map< PyObject*, PyObject* >::iterator iscbp = gSizeCallbacks.find( self );
68  if ( iscbp != gSizeCallbacks.end() ) {
69  PyObject* pylen = PyObject_CallObject( iscbp->second, NULL );
70  Py_ssize_t nlen2 = PyInt_AsSsize_t( pylen );
71  Py_DECREF( pylen );
72 
73  if ( nlen2 == (Py_ssize_t)-1 && PyErr_Occurred() )
74  PyErr_Clear();
75  else
76  return nlen2;
77  }
78 
79  return nlen; // return nlen after all, since have nothing better
80  }
81 
82 ////////////////////////////////////////////////////////////////////////////////
83 /// Retrieve the buffer as a linear char array.
84 
85  const char* buffer_get( PyObject* self, int idx )
86  {
87  if ( idx < 0 || idx >= buffer_length( self ) ) {
88  PyErr_SetString( PyExc_IndexError, "buffer index out of range" );
89  return 0;
90  }
91 
92 #if PY_VERSION_HEX < 0x02050000
93  const char* buf = 0;
94 #else
95  char* buf = 0; // interface change in 2.5, no other way to handle it
96 #endif
97 #if PY_VERSION_HEX < 0x03000000
98  (*(PyBuffer_Type.tp_as_buffer->bf_getcharbuffer))( self, 0, &buf );
99 #else
100  Py_buffer bufinfo;
101  (*(PyBuffer_Type.tp_as_buffer->bf_getbuffer))( self, &bufinfo, PyBUF_SIMPLE );
102  buf = (char*)bufinfo.buf;
103 #endif
104 
105  if ( ! buf )
106  PyErr_SetString( PyExc_IndexError, "attempt to index a null-buffer" );
107 
108  return buf;
109  }
110 
111 ////////////////////////////////////////////////////////////////////////////////
112 
113 #define PYROOT_IMPLEMENT_PYBUFFER_METHODS( name, type, stype, F1, F2 ) \
114  PyObject* name##_buffer_str( PyObject* self ) \
115  { \
116  Py_ssize_t l = buffer_length( self ); \
117  return PyROOT_PyUnicode_FromFormat( "<"#type" buffer, size " PY_SSIZE_T_FORMAT ">", l );\
118  } \
119  \
120  PyObject* name##_buffer_item( PyObject* self, Py_ssize_t idx ) { \
121  const char* buf = buffer_get( self, idx ); \
122  if ( buf ) \
123  return F1( (stype)*((type*)buf + idx) ); \
124  return 0; \
125  } \
126  \
127  int name##_buffer_ass_item( PyObject* self, Py_ssize_t idx, PyObject* val ) {\
128  const char* buf = buffer_get( self, idx ); \
129  if ( ! buf ) \
130  return -1; \
131  \
132  type value = F2( val ); \
133  if ( value == (type)-1 && PyErr_Occurred() ) \
134  return -1; \
135  \
136  *((type*)buf+idx) = (type)value; \
137  return 0; \
138  } \
139  \
140  PyObject* name##_buffer_subscript( PyObject* self, PyObject* item ) { \
141  if ( PyIndex_Check( item ) ) { \
142  Py_ssize_t idx = PyNumber_AsSsize_t( item, PyExc_IndexError ); \
143  if ( idx == -1 && PyErr_Occurred() ) \
144  return 0; \
145  return name##_buffer_item( self, idx ); \
146  } \
147  return 0; \
148  }
149 
151  PYROOT_IMPLEMENT_PYBUFFER_METHODS( Short, Short_t, Long_t, PyInt_FromLong, PyInt_AsLong )
152  PYROOT_IMPLEMENT_PYBUFFER_METHODS( UShort, UShort_t, Long_t, PyInt_FromLong, PyInt_AsLong )
153  PYROOT_IMPLEMENT_PYBUFFER_METHODS( Int, Int_t, Long_t, PyInt_FromLong, PyInt_AsLong )
154  PYROOT_IMPLEMENT_PYBUFFER_METHODS( UInt, UInt_t, Long_t, PyInt_FromLong, PyInt_AsLong )
155  PYROOT_IMPLEMENT_PYBUFFER_METHODS( Long, Long_t, Long_t, PyLong_FromLong, PyLong_AsLong )
156  PYROOT_IMPLEMENT_PYBUFFER_METHODS( ULong, ULong_t, ULong_t, PyLong_FromUnsignedLong, PyLong_AsUnsignedLong )
157  PYROOT_IMPLEMENT_PYBUFFER_METHODS( Float, Float_t, Double_t, PyFloat_FromDouble, PyFloat_AsDouble )
158  PYROOT_IMPLEMENT_PYBUFFER_METHODS( Double, Double_t, Double_t, PyFloat_FromDouble, PyFloat_AsDouble )
159 
160  int pyroot_buffer_ass_subscript( PyObject* self, PyObject* idx, PyObject* val ) {
161  // Assign the given value 'val' to the item at index 'idx.'
162  if ( PyIndex_Check( idx ) ) {
163  Py_ssize_t i = PyNumber_AsSsize_t( idx, PyExc_IndexError );
164  if ( i == -1 && PyErr_Occurred() )
165  return -1;
166  return Py_TYPE(self)->tp_as_sequence->sq_ass_item( self, i, val );
167  } else {
168  PyErr_SetString( PyExc_TypeError, "buffer indices must be integers" );
169  return -1;
170  }
171  }
172 
173 
174 ////////////////////////////////////////////////////////////////////////////////
175 /// Allow the user to fix up the actual (type-strided) size of the buffer.
176 
177  PyObject* buffer_setsize( PyObject* self, PyObject* pynlen )
178  {
179  Py_ssize_t nlen = PyInt_AsSsize_t( pynlen );
180  if ( nlen == -1 && PyErr_Occurred() )
181  return 0;
182 
183 #if PY_VERSION_HEX < 0x03000000
184  ((PyBufferTop_t*)self)->fSize = nlen;
185 #else
186  PyMemoryView_GET_BUFFER(self)->len = nlen;
187 #endif
188 
189  Py_INCREF( Py_None );
190  return Py_None;
191  }
192 
193 ////////////////////////////////////////////////////////////////////////////////
194 /// return a typecode in the style of module array
195 
196  PyObject* buf_typecode( PyObject* pyobject, void* )
197  {
198  if ( PyObject_TypeCheck( pyobject, &PyBoolBuffer_Type ) )
199  return PyROOT_PyUnicode_FromString( (char*)"b" );
200  else if ( PyObject_TypeCheck( pyobject, &PyShortBuffer_Type ) )
201  return PyROOT_PyUnicode_FromString( (char*)"h" );
202  else if ( PyObject_TypeCheck( pyobject, &PyUShortBuffer_Type ) )
203  return PyROOT_PyUnicode_FromString( (char*)"H" );
204  else if ( PyObject_TypeCheck( pyobject, &PyIntBuffer_Type ) )
205  return PyROOT_PyUnicode_FromString( (char*)"i" );
206  else if ( PyObject_TypeCheck( pyobject, &PyUIntBuffer_Type ) )
207  return PyROOT_PyUnicode_FromString( (char*)"I" );
208  else if ( PyObject_TypeCheck( pyobject, &PyLongBuffer_Type ) )
209  return PyROOT_PyUnicode_FromString( (char*)"l" );
210  else if ( PyObject_TypeCheck( pyobject, &PyULongBuffer_Type ) )
211  return PyROOT_PyUnicode_FromString( (char*)"L" );
212  else if ( PyObject_TypeCheck( pyobject, &PyFloatBuffer_Type ) )
213  return PyROOT_PyUnicode_FromString( (char*)"f" );
214  else if ( PyObject_TypeCheck( pyobject, &PyDoubleBuffer_Type ) )
215  return PyROOT_PyUnicode_FromString( (char*)"d" );
216 
217  PyErr_SetString( PyExc_TypeError, "received unknown buffer object" );
218  return 0;
219  }
220 
221 ////////////////////////////////////////////////////////////////////////////////
222 
223  PyGetSetDef buffer_getset[] = {
224  { (char*)"typecode", (getter)buf_typecode, NULL, NULL, NULL },
225  { (char*)NULL, NULL, NULL, NULL, NULL }
226  };
227 
228 ////////////////////////////////////////////////////////////////////////////////
229 
230  PyMethodDef buffer_methods[] = {
231  { (char*)"SetSize", (PyCFunction)buffer_setsize, METH_O, NULL },
232  { (char*)NULL, NULL, 0, NULL }
233  };
234 
235 } // unnamed namespace
236 
237 
238 //- instance handler ------------------------------------------------------------
240 {
241 // singleton factory
242  static TPyBufferFactory* fac = new TPyBufferFactory;
243  return fac;
244 }
245 
246 
247 //- constructor/destructor ------------------------------------------------------
248 #define PYROOT_INSTALL_PYBUFFER_METHODS( name, type ) \
249  Py##name##Buffer_Type.tp_name = (char*)"ROOT.Py"#name"Buffer"; \
250  Py##name##Buffer_Type.tp_base = &PyBuffer_Type; \
251  Py##name##Buffer_Type.tp_as_buffer = PyBuffer_Type.tp_as_buffer; \
252  Py##name##Buffer_SeqMethods.sq_item = (ssizeargfunc)name##_buffer_item; \
253  Py##name##Buffer_SeqMethods.sq_ass_item = (ssizeobjargproc)name##_buffer_ass_item;\
254  Py##name##Buffer_SeqMethods.sq_length = (lenfunc)buffer_length; \
255  Py##name##Buffer_Type.tp_as_sequence = &Py##name##Buffer_SeqMethods; \
256  if ( PyBuffer_Type.tp_as_mapping ) { /* p2.6 and later */ \
257  Py##name##Buffer_MapMethods.mp_length = (lenfunc)buffer_length; \
258  Py##name##Buffer_MapMethods.mp_subscript = (binaryfunc)name##_buffer_subscript;\
259  Py##name##Buffer_MapMethods.mp_ass_subscript = (objobjargproc)pyroot_buffer_ass_subscript;\
260  Py##name##Buffer_Type.tp_as_mapping = &Py##name##Buffer_MapMethods; \
261  } \
262  Py##name##Buffer_Type.tp_str = (reprfunc)name##_buffer_str; \
263  Py##name##Buffer_Type.tp_methods = buffer_methods; \
264  Py##name##Buffer_Type.tp_getset = buffer_getset; \
265  PyType_Ready( &Py##name##Buffer_Type );
266 
268 {
269 // construct python buffer types
279 }
280 
281 ////////////////////////////////////////////////////////////////////////////////
282 
284 {
285 }
286 
287 
288 //- public members --------------------------------------------------------------
289 #define PYROOT_IMPLEMENT_PYBUFFER_FROM_MEMORY( name, type ) \
290 PyObject* PyROOT::TPyBufferFactory::PyBuffer_FromMemory( type* address, Py_ssize_t size )\
291 { \
292  size = size < 0 ? INT_MAX : size; \
293  PyObject* buf = PyBuffer_FromReadWriteMemory( (void*)address, size ); \
294  if ( buf ) { \
295  Py_INCREF( (PyObject*)(void*)&Py##name##Buffer_Type ); \
296  buf->ob_type = &Py##name##Buffer_Type; \
297  } \
298  return buf; \
299 } \
300  \
301 PyObject* PyROOT::TPyBufferFactory::PyBuffer_FromMemory( type* address, PyObject* scb )\
302 { \
303  PyObject* buf = PyBuffer_FromMemory( address, Py_ssize_t(0) ); \
304  if ( buf != 0 && PyCallable_Check( scb ) ) { \
305  Py_INCREF( scb ); \
306  gSizeCallbacks[ buf ] = scb; \
307  } \
308  return buf; \
309 }
310 
#define PyROOT_PyUnicode_FromString
Definition: PyROOT.h:71
float Float_t
Definition: RtypesCore.h:53
unsigned short UShort_t
Definition: RtypesCore.h:36
int Int_t
Definition: RtypesCore.h:41
bool Bool_t
Definition: RtypesCore.h:59
Py_ssize_t PyNumber_AsSsize_t(PyObject *obj, PyObject *)
Definition: PyROOT.h:169
if(pyself &&pyself!=Py_None)
#define PYROOT_INSTALL_PYBUFFER_METHODS(name, type)
#define PyInt_AsSsize_t
Definition: PyROOT.h:155
unsigned int UInt_t
Definition: RtypesCore.h:42
short Short_t
Definition: RtypesCore.h:35
long Long_t
Definition: RtypesCore.h:50
double Double_t
Definition: RtypesCore.h:55
unsigned long ULong_t
Definition: RtypesCore.h:51
Factory for python buffers of non-string type.
const char * Bool
#define Py_TYPE(ob)
Definition: PyROOT.h:149
#define PYROOT_IMPLEMENT_PYBUFFER_FROM_MEMORY(name, type)
int Py_ssize_t
Definition: PyROOT.h:154
static TPyBufferFactory * Instance()
#define PyBool_FromLong
Definition: PyROOT.h:190
#define NULL
Definition: Rtypes.h:82
#define PyIndex_Check(obj)
Definition: PyROOT.h:166
#define PYROOT_PREPARE_PYBUFFER_TYPE(name)
#define PYROOT_IMPLEMENT_PYBUFFER_METHODS(name, type, stype, F1, F2)
_object PyObject
Definition: TPyArg.h:22