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