Logo ROOT   6.30.04
Reference Guide
 All Namespaces Files Pages
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 #if PY_VERSION_HEX > 0x03000000
14  if ( !ptr ) { // p3 will set an exception if nullptr, just rely on size == 0
15  static long dummy[1];
16  ptr = dummy;
17  size = 0;
18  }
19 #endif
20 
21  Py_buffer bufinfo = { ptr, NULL, size, 1, 0, 1, NULL, NULL, NULL, NULL,
22 #if PY_VERSION_HEX < 0x03030000
23  { 0, 0 },
24 #endif
25  NULL };
26  return PyMemoryView_FromBuffer( &bufinfo );
27 }
28 #endif
29 
30 
31 //- data ---------------------------------------------------------------------
32 namespace {
33 
34 // top of buffer (rest of buffer object has changed across python versions)
35  struct PyBufferTop_t {
36  PyObject_HEAD
37  PyObject* fBase; // b_base in python
38  void* fPtr; // b_ptr in python
39  Py_ssize_t fSize; // b_size in python
40  Py_ssize_t fItemSize; // b_itemsize in python
41  };
42 
43 // callable cache
44  std::map< PyObject*, PyObject* > gSizeCallbacks;
45 
46 // create buffer types and copy methods to be adjusted
47 #define PYROOT_PREPARE_PYBUFFER_TYPE( name ) \
48  PyTypeObject Py##name##Buffer_Type; \
49  PySequenceMethods Py##name##Buffer_SeqMethods = *(PyBuffer_Type.tp_as_sequence);\
50  PyMappingMethods Py##name##Buffer_MapMethods;
51 
52  PYROOT_PREPARE_PYBUFFER_TYPE( Bool )
53  PYROOT_PREPARE_PYBUFFER_TYPE( Char )
54  PYROOT_PREPARE_PYBUFFER_TYPE( UChar )
55  PYROOT_PREPARE_PYBUFFER_TYPE( Short )
56  PYROOT_PREPARE_PYBUFFER_TYPE( UShort )
57  PYROOT_PREPARE_PYBUFFER_TYPE( Int )
58  PYROOT_PREPARE_PYBUFFER_TYPE( UInt )
59  PYROOT_PREPARE_PYBUFFER_TYPE( Long )
60  PYROOT_PREPARE_PYBUFFER_TYPE( ULong )
61  PYROOT_PREPARE_PYBUFFER_TYPE( Float )
62  PYROOT_PREPARE_PYBUFFER_TYPE( Double )
63 
64 // implement get, str, and length functions
65  Py_ssize_t buffer_length( PyObject* self )
66  {
67  // Retrieve the (type-strided) size of the the buffer; may be a guess.
68 #if PY_VERSION_HEX < 0x03000000
69  Py_ssize_t nlen = ((PyBufferTop_t*)self)->fSize;
70  Py_ssize_t item = ((PyBufferTop_t*)self)->fItemSize;
71 #else
72  Py_buffer* bufinfo = PyMemoryView_GET_BUFFER(self);
73  Py_ssize_t nlen = bufinfo->len;
74  Py_ssize_t item = bufinfo->itemsize;
75 #endif
76  if ( nlen != INT_MAX ) // INT_MAX is the default, i.e. unknown actual length
77  return nlen/item;
78 
79  std::map< PyObject*, PyObject* >::iterator iscbp = gSizeCallbacks.find( self );
80  if ( iscbp != gSizeCallbacks.end() ) {
81  PyObject* pylen = PyObject_CallObject( iscbp->second, NULL );
82  Py_ssize_t nlen2 = PyInt_AsSsize_t( pylen );
83  Py_DECREF( pylen );
84 
85  if ( nlen2 == (Py_ssize_t)-1 && PyErr_Occurred() )
86  PyErr_Clear();
87  else
88  return nlen2;
89  }
90 
91  return nlen; // return nlen after all, since have nothing better
92  }
93 
94 ////////////////////////////////////////////////////////////////////////////////
95 /// Retrieve the buffer as a linear char array.
96 
97  const char* buffer_get( PyObject* self, int idx )
98  {
99  if ( idx < 0 || idx >= buffer_length( self ) ) {
100  PyErr_SetString( PyExc_IndexError, "buffer index out of range" );
101  return 0;
102  }
103 
104 #if PY_VERSION_HEX < 0x02050000
105  const char* buf = 0;
106 #else
107  char* buf = 0; // interface change in 2.5, no other way to handle it
108 #endif
109 #if PY_VERSION_HEX < 0x03000000
110  (*(PyBuffer_Type.tp_as_buffer->bf_getcharbuffer))( self, 0, &buf );
111 #else
112  Py_buffer bufinfo;
113  (*(PyBuffer_Type.tp_as_buffer->bf_getbuffer))( self, &bufinfo, PyBUF_SIMPLE );
114  buf = (char*)bufinfo.buf;
115  (*(PyBuffer_Type.tp_as_buffer->bf_releasebuffer))(self, &bufinfo);
116  Py_DECREF(bufinfo.obj);
117 #endif
118 
119  if ( ! buf )
120  PyErr_SetString( PyExc_IndexError, "attempt to index a null-buffer" );
121 
122  return buf;
123  }
124 
125 ////////////////////////////////////////////////////////////////////////////////
126 
127 #define PYROOT_IMPLEMENT_PYBUFFER_METHODS( name, type, stype, F1, F2 ) \
128  PyObject* name##_buffer_str( PyObject* self ) \
129  { \
130  Py_ssize_t l = buffer_length( self ); \
131  return PyROOT_PyUnicode_FromFormat( "<"#type" buffer, size " PY_SSIZE_T_FORMAT ">", l );\
132  } \
133  \
134  PyObject* name##_buffer_item( PyObject* self, Py_ssize_t idx ) { \
135  const char* buf = buffer_get( self, idx ); \
136  if ( buf ) \
137  return F1( (stype)*((type*)buf + idx) ); \
138  return 0; \
139  } \
140  \
141  int name##_buffer_ass_item( PyObject* self, Py_ssize_t idx, PyObject* val ) {\
142  const char* buf = buffer_get( self, idx ); \
143  if ( ! buf ) \
144  return -1; \
145  \
146  type value = F2( val ); \
147  if ( value == (type)-1 && PyErr_Occurred() ) \
148  return -1; \
149  \
150  *((type*)buf+idx) = (type)value; \
151  return 0; \
152  } \
153  \
154  PyObject* name##_buffer_subscript( PyObject* self, PyObject* item ) { \
155  if ( PyIndex_Check( item ) ) { \
156  Py_ssize_t idx = PyNumber_AsSsize_t( item, PyExc_IndexError ); \
157  if ( idx == -1 && PyErr_Occurred() ) \
158  return 0; \
159  return name##_buffer_item( self, idx ); \
160  } \
161  return 0; \
162  }
163 
164  PYROOT_IMPLEMENT_PYBUFFER_METHODS( Bool, Bool_t, Long_t, PyBool_FromLong, PyInt_AsLong )
165  PYROOT_IMPLEMENT_PYBUFFER_METHODS( Char, Char_t, Long_t, PyInt_FromLong, PyInt_AsLong )
166  PYROOT_IMPLEMENT_PYBUFFER_METHODS( UChar, UChar_t, Long_t, PyInt_FromLong, PyInt_AsLong )
167  PYROOT_IMPLEMENT_PYBUFFER_METHODS( Short, Short_t, Long_t, PyInt_FromLong, PyInt_AsLong )
168  PYROOT_IMPLEMENT_PYBUFFER_METHODS( UShort, UShort_t, Long_t, PyInt_FromLong, PyInt_AsLong )
169  PYROOT_IMPLEMENT_PYBUFFER_METHODS( Int, Int_t, Long_t, PyInt_FromLong, PyInt_AsLong )
170  PYROOT_IMPLEMENT_PYBUFFER_METHODS( UInt, UInt_t, Long_t, PyInt_FromLong, PyInt_AsLong )
171  PYROOT_IMPLEMENT_PYBUFFER_METHODS( Long, Long_t, Long_t, PyLong_FromLong, PyLong_AsLong )
172  PYROOT_IMPLEMENT_PYBUFFER_METHODS( ULong, ULong_t, ULong_t, PyLong_FromUnsignedLong, PyLong_AsUnsignedLong )
173  PYROOT_IMPLEMENT_PYBUFFER_METHODS( Float, Float_t, Double_t, PyFloat_FromDouble, PyFloat_AsDouble )
174  PYROOT_IMPLEMENT_PYBUFFER_METHODS( Double, Double_t, Double_t, PyFloat_FromDouble, PyFloat_AsDouble )
175 
176  int pyroot_buffer_ass_subscript( PyObject* self, PyObject* idx, PyObject* val ) {
177  // Assign the given value 'val' to the item at index 'idx.'
178  if ( PyIndex_Check( idx ) ) {
179  Py_ssize_t i = PyNumber_AsSsize_t( idx, PyExc_IndexError );
180  if ( i == -1 && PyErr_Occurred() )
181  return -1;
182  return Py_TYPE(self)->tp_as_sequence->sq_ass_item( self, i, val );
183  } else {
184  PyErr_SetString( PyExc_TypeError, "buffer indices must be integers" );
185  return -1;
186  }
187  }
188 
189 
190 ////////////////////////////////////////////////////////////////////////////////
191 /// Allow the user to fix up the actual (type-strided) size of the buffer.
192 
193  PyObject* buffer_setsize( PyObject* self, PyObject* pynlen )
194  {
195  Py_ssize_t nlen = PyInt_AsSsize_t( pynlen );
196  if ( nlen == -1 && PyErr_Occurred() )
197  return 0;
198 
199 #if PY_VERSION_HEX < 0x03000000
200  ((PyBufferTop_t*)self)->fSize = nlen * ((PyBufferTop_t*)self)->fItemSize;
201 #else
202  PyMemoryView_GET_BUFFER(self)->len = nlen * PyMemoryView_GET_BUFFER(self)->itemsize;
203 #endif
204 
205  Py_INCREF( Py_None );
206  return Py_None;
207  }
208 
209  PyObject *SetSize(PyObject *self, PyObject *pynlen)
210  {
211  if (PyErr_WarnEx(PyExc_FutureWarning,
212  "buffer.SetSize(N) is deprecated and will disappear in a future version of ROOT. "
213  "Instead, use buffer.reshape((N,))", 1) < 0) {
214  return nullptr;
215  }
216 
217  return buffer_setsize(self, pynlen);
218  }
219 
220  PyObject *reshape(PyObject *self, PyObject *shape)
221  {
222  // Allow the user to fix up the actual (type-strided) size of the buffer.
223  if (!PyTuple_Check(shape) || PyTuple_GET_SIZE(shape) != 1) {
224  if (shape) {
225  PyObject* pystr = PyObject_Str(shape);
226  if (pystr) {
227  PyErr_Format(PyExc_TypeError, "tuple object of length 1 expected, received %s",
228  PyROOT_PyUnicode_AsStringChecked(pystr));
229  Py_DECREF(pystr);
230  return nullptr;
231  }
232  }
233  PyErr_SetString(PyExc_TypeError, "tuple object of length 1 expected");
234  return nullptr;
235  }
236 
237  return buffer_setsize(self, PyTuple_GET_ITEM(shape, 0));
238  }
239 
240 ////////////////////////////////////////////////////////////////////////////////
241 /// return a typecode in the style of module array
242 
243  PyObject* buf_typecode( PyObject* pyobject, void* )
244  {
245  if ( PyObject_TypeCheck( pyobject, &PyBoolBuffer_Type ) )
246  return PyROOT_PyUnicode_FromString( (char*)"b" );
247  else if ( PyObject_TypeCheck( pyobject, &PyCharBuffer_Type ) )
248  return PyROOT_PyUnicode_FromString( (char*)"b" );
249  else if ( PyObject_TypeCheck( pyobject, &PyUCharBuffer_Type ) )
250  return PyROOT_PyUnicode_FromString( (char*)"B" );
251  else if ( PyObject_TypeCheck( pyobject, &PyShortBuffer_Type ) )
252  return PyROOT_PyUnicode_FromString( (char*)"h" );
253  else if ( PyObject_TypeCheck( pyobject, &PyUShortBuffer_Type ) )
254  return PyROOT_PyUnicode_FromString( (char*)"H" );
255  else if ( PyObject_TypeCheck( pyobject, &PyIntBuffer_Type ) )
256  return PyROOT_PyUnicode_FromString( (char*)"i" );
257  else if ( PyObject_TypeCheck( pyobject, &PyUIntBuffer_Type ) )
258  return PyROOT_PyUnicode_FromString( (char*)"I" );
259  else if ( PyObject_TypeCheck( pyobject, &PyLongBuffer_Type ) )
260  return PyROOT_PyUnicode_FromString( (char*)"l" );
261  else if ( PyObject_TypeCheck( pyobject, &PyULongBuffer_Type ) )
262  return PyROOT_PyUnicode_FromString( (char*)"L" );
263  else if ( PyObject_TypeCheck( pyobject, &PyFloatBuffer_Type ) )
264  return PyROOT_PyUnicode_FromString( (char*)"f" );
265  else if ( PyObject_TypeCheck( pyobject, &PyDoubleBuffer_Type ) )
266  return PyROOT_PyUnicode_FromString( (char*)"d" );
267 
268  PyErr_SetString( PyExc_TypeError, "received unknown buffer object" );
269  return 0;
270  }
271 
272 ////////////////////////////////////////////////////////////////////////////////
273 
274  PyGetSetDef buffer_getset[] = {
275  { (char*)"typecode", (getter)buf_typecode, NULL, NULL, NULL },
276  { (char*)NULL, NULL, NULL, NULL, NULL }
277  };
278 
279 ////////////////////////////////////////////////////////////////////////////////
280 
281  PyMethodDef buffer_methods[] = {
282  { (char*)"SetSize", (PyCFunction)SetSize, METH_O, NULL },
283  { (char*)"reshape", (PyCFunction)reshape, METH_O, NULL },
284  { (char*)NULL, NULL, 0, NULL }
285  };
286 
287 } // unnamed namespace
288 
289 
290 //- instance handler ------------------------------------------------------------
291 PyROOT::TPyBufferFactory* PyROOT::TPyBufferFactory::Instance()
292 {
293 // singleton factory
294  static TPyBufferFactory* fac = new TPyBufferFactory;
295  return fac;
296 }
297 
298 
299 //- constructor/destructor ------------------------------------------------------
300 #define PYROOT_INSTALL_PYBUFFER_METHODS( name, type ) \
301  Py##name##Buffer_Type.tp_name = (char*)"ROOT.Py"#name"Buffer"; \
302  Py##name##Buffer_Type.tp_base = &PyBuffer_Type; \
303  Py##name##Buffer_Type.tp_as_buffer = PyBuffer_Type.tp_as_buffer; \
304  Py##name##Buffer_SeqMethods.sq_item = (ssizeargfunc)name##_buffer_item; \
305  Py##name##Buffer_SeqMethods.sq_ass_item = (ssizeobjargproc)name##_buffer_ass_item;\
306  Py##name##Buffer_SeqMethods.sq_length = (lenfunc)buffer_length; \
307  Py##name##Buffer_Type.tp_as_sequence = &Py##name##Buffer_SeqMethods; \
308  if ( PyBuffer_Type.tp_as_mapping ) { /* p2.6 and later */ \
309  Py##name##Buffer_MapMethods.mp_length = (lenfunc)buffer_length; \
310  Py##name##Buffer_MapMethods.mp_subscript = (binaryfunc)name##_buffer_subscript;\
311  Py##name##Buffer_MapMethods.mp_ass_subscript = (objobjargproc)pyroot_buffer_ass_subscript;\
312  Py##name##Buffer_Type.tp_as_mapping = &Py##name##Buffer_MapMethods; \
313  } \
314  Py##name##Buffer_Type.tp_str = (reprfunc)name##_buffer_str; \
315  Py##name##Buffer_Type.tp_methods = buffer_methods; \
316  Py##name##Buffer_Type.tp_getset = buffer_getset; \
317  PyType_Ready( &Py##name##Buffer_Type );
318 
319 PyROOT::TPyBufferFactory::TPyBufferFactory()
320 {
321 // construct python buffer types
322  PYROOT_INSTALL_PYBUFFER_METHODS( Bool, Bool_t )
323  PYROOT_INSTALL_PYBUFFER_METHODS( Char, Char_t )
324  PYROOT_INSTALL_PYBUFFER_METHODS( UChar, UChar_t )
325  PYROOT_INSTALL_PYBUFFER_METHODS( Short, Short_t )
326  PYROOT_INSTALL_PYBUFFER_METHODS( UShort, UShort_t )
327  PYROOT_INSTALL_PYBUFFER_METHODS( Int, Int_t )
328  PYROOT_INSTALL_PYBUFFER_METHODS( UInt, UInt_t )
329  PYROOT_INSTALL_PYBUFFER_METHODS( Long, Long_t )
330  PYROOT_INSTALL_PYBUFFER_METHODS( ULong, ULong_t )
331  PYROOT_INSTALL_PYBUFFER_METHODS( Float, Float_t )
332  PYROOT_INSTALL_PYBUFFER_METHODS( Double, Double_t )
333 }
334 
335 ////////////////////////////////////////////////////////////////////////////////
336 
337 PyROOT::TPyBufferFactory::~TPyBufferFactory()
338 {
339 }
340 
341 const char* getBoolFormat() { return "b";}
342 const char* getCharFormat() { return "b";}
343 const char* getUCharFormat() { return "B";}
344 const char* getShortFormat() { return "h";}
345 const char* getUShortFormat() { return "H";}
346 const char* getIntFormat() { return "i";}
347 const char* getUIntFormat() { return "I";}
348 const char* getLongFormat() { return "l";}
349 const char* getULongFormat() { return "L";}
350 const char* getFloatFormat() { return "f";}
351 const char* getDoubleFormat() { return "d";}
352 
353 #if PY_VERSION_HEX < 0x03000000
354  #define PYBUFFER_SETITEMSIZE(buf,type) ((PyBufferTop_t*)buf)->fItemSize = Py_ssize_t(sizeof(type))
355  #define PYBUFFER_SETFORMAT(buf,name)
356 #else
357  #define PYBUFFER_SETITEMSIZE(buf,type) PyMemoryView_GET_BUFFER(buf)->itemsize = Py_ssize_t(sizeof(type))
358  #define PYBUFFER_SETFORMAT(buf,name) PyMemoryView_GET_BUFFER(buf)->format = (char *)get##name##Format()
359 #endif
360 
361 //- public members --------------------------------------------------------------
362 #define PYROOT_IMPLEMENT_PYBUFFER_FROM_MEMORY( name, type ) \
363 PyObject* PyROOT::TPyBufferFactory::PyBuffer_FromMemory( type* address, Py_ssize_t size )\
364 { \
365  size = size < 0 ? INT_MAX : size; \
366  PyObject* buf = PyBuffer_FromReadWriteMemory( (void*)address, size ); \
367  if ( buf ) { \
368  Py_INCREF( (PyObject*)(void*)&Py##name##Buffer_Type ); \
369  buf->ob_type = &Py##name##Buffer_Type; \
370  PYBUFFER_SETITEMSIZE(buf,type); \
371  PYBUFFER_SETFORMAT(buf, name); \
372  } \
373  return buf; \
374 } \
375  \
376 PyObject* PyROOT::TPyBufferFactory::PyBuffer_FromMemory( type* address, PyObject* scb )\
377 { \
378  PyObject* buf = PyBuffer_FromMemory( address, Py_ssize_t(0) ); \
379  if ( buf != 0 && PyCallable_Check( scb ) ) { \
380  Py_INCREF( scb ); \
381  gSizeCallbacks[ buf ] = scb; \
382  } \
383  return buf; \
384 }
385 
386 PYROOT_IMPLEMENT_PYBUFFER_FROM_MEMORY( Bool, Bool_t )
387 PYROOT_IMPLEMENT_PYBUFFER_FROM_MEMORY( Char, Char_t )
388 PYROOT_IMPLEMENT_PYBUFFER_FROM_MEMORY( UChar, UChar_t )
389 PYROOT_IMPLEMENT_PYBUFFER_FROM_MEMORY( Short, Short_t )
390 PYROOT_IMPLEMENT_PYBUFFER_FROM_MEMORY( UShort, UShort_t )
391 PYROOT_IMPLEMENT_PYBUFFER_FROM_MEMORY( Int, Int_t )
392 PYROOT_IMPLEMENT_PYBUFFER_FROM_MEMORY( UInt, UInt_t )
393 PYROOT_IMPLEMENT_PYBUFFER_FROM_MEMORY( Long, Long_t )
394 PYROOT_IMPLEMENT_PYBUFFER_FROM_MEMORY( ULong, ULong_t )
395 PYROOT_IMPLEMENT_PYBUFFER_FROM_MEMORY( Float, Float_t )
396 PYROOT_IMPLEMENT_PYBUFFER_FROM_MEMORY( Double, Double_t )