27
27
28
28
#include < Python.h>
29
29
#include < numpy/ndarrayobject.h>
30
+ #include < stdexcept>
31
+
32
+ #if defined(__GNUC__) || defined(__clang__)
33
+ #define unlikely (x ) __builtin_expect(!!(x), 0 )
34
+ #else
35
+ #define unlikely (x ) (x)
36
+ #endif
30
37
31
38
namespace numpy
32
39
{
@@ -235,27 +242,39 @@ class array_view_accessors<AV, T, 1>
235
242
T &operator ()(npy_intp i)
236
243
{
237
244
AVC *self = static_cast <AVC *>(this );
245
+ if (unlikely (self->m_empty )) {
246
+ throw std::out_of_range (" indexing into an array_view with no data" );
247
+ }
238
248
239
249
return *reinterpret_cast <T *>(self->m_data + self->m_strides [0 ] * i);
240
250
}
241
251
242
252
const T &operator ()(npy_intp i) const
243
253
{
244
254
const AVC *self = static_cast <const AVC *>(this );
255
+ if (unlikely (self->m_empty )) {
256
+ throw std::out_of_range (" indexing into an array_view with no data" );
257
+ }
245
258
246
259
return *reinterpret_cast <const T *>(self->m_data + self->m_strides [0 ] * i);
247
260
}
248
261
249
262
T &operator [](npy_intp i)
250
263
{
251
264
AVC *self = static_cast <AVC *>(this );
265
+ if (unlikely (self->m_empty )) {
266
+ throw std::out_of_range (" indexing into an array_view with no data" );
267
+ }
252
268
253
269
return *reinterpret_cast <T *>(self->m_data + self->m_strides [0 ] * i);
254
270
}
255
271
256
272
const T &operator [](npy_intp i) const
257
273
{
258
274
const AVC *self = static_cast <const AVC *>(this );
275
+ if (unlikely (self->m_empty )) {
276
+ throw std::out_of_range (" indexing into an array_view with no data" );
277
+ }
259
278
260
279
return *reinterpret_cast <const T *>(self->m_data + self->m_strides [0 ] * i);
261
280
}
@@ -271,6 +290,9 @@ class array_view_accessors<AV, T, 2>
271
290
T &operator ()(npy_intp i, npy_intp j)
272
291
{
273
292
AVC *self = static_cast <AVC *>(this );
293
+ if (unlikely (self->m_empty )) {
294
+ throw std::out_of_range (" indexing into an array_view with no data" );
295
+ }
274
296
275
297
return *reinterpret_cast <T *>(self->m_data + self->m_strides [0 ] * i +
276
298
self->m_strides [1 ] * j);
@@ -279,6 +301,9 @@ class array_view_accessors<AV, T, 2>
279
301
const T &operator ()(npy_intp i, npy_intp j) const
280
302
{
281
303
const AVC *self = static_cast <const AVC *>(this );
304
+ if (unlikely (self->m_empty )) {
305
+ throw std::out_of_range (" indexing into an array_view with no data" );
306
+ }
282
307
283
308
return *reinterpret_cast <const T *>(self->m_data + self->m_strides [0 ] * i +
284
309
self->m_strides [1 ] * j);
@@ -287,6 +312,9 @@ class array_view_accessors<AV, T, 2>
287
312
sub_t operator [](npy_intp i) const
288
313
{
289
314
const AVC *self = static_cast <const AVC *>(this );
315
+ if (unlikely (self->m_empty )) {
316
+ throw std::out_of_range (" indexing into an array_view with no data" );
317
+ }
290
318
291
319
return sub_t (self->m_arr ,
292
320
self->m_data + self->m_strides [0 ] * i,
@@ -305,6 +333,9 @@ class array_view_accessors<AV, T, 3>
305
333
T &operator ()(npy_intp i, npy_intp j, npy_intp k)
306
334
{
307
335
AVC *self = static_cast <AVC *>(this );
336
+ if (unlikely (self->m_empty )) {
337
+ throw std::out_of_range (" indexing into an array_view with no data" );
338
+ }
308
339
309
340
return *reinterpret_cast <T *>(self->m_data + self->m_strides [0 ] * i +
310
341
self->m_strides [1 ] * j + self->m_strides [2 ] * k);
@@ -313,6 +344,9 @@ class array_view_accessors<AV, T, 3>
313
344
const T &operator ()(npy_intp i, npy_intp j, npy_intp k) const
314
345
{
315
346
const AVC *self = static_cast <const AVC *>(this );
347
+ if (unlikely (self->m_empty )) {
348
+ throw std::out_of_range (" indexing into an array_view with no data" );
349
+ }
316
350
317
351
return *reinterpret_cast <const T *>(self->m_data + self->m_strides [0 ] * i +
318
352
self->m_strides [1 ] * j + self->m_strides [2 ] * k);
@@ -321,6 +355,9 @@ class array_view_accessors<AV, T, 3>
321
355
sub_t operator [](npy_intp i) const
322
356
{
323
357
const AVC *self = static_cast <const AVC *>(this );
358
+ if (unlikely (self->m_empty )) {
359
+ throw std::out_of_range (" indexing into an array_view with no data" );
360
+ }
324
361
325
362
return sub_t (self->m_arr ,
326
363
self->m_data + self->m_strides [0 ] * i,
@@ -349,6 +386,9 @@ class array_view : public detail::array_view_accessors<array_view, T, ND>
349
386
npy_intp *m_shape;
350
387
npy_intp *m_strides;
351
388
char *m_data;
389
+ // Flag for a limited kind of bounds checking,
390
+ // not the same as the empty() method which checks the outer dimension
391
+ bool m_empty;
352
392
353
393
public:
354
394
typedef T value_type;
@@ -361,6 +401,7 @@ class array_view : public detail::array_view_accessors<array_view, T, ND>
361
401
{
362
402
m_shape = zeros;
363
403
m_strides = zeros;
404
+ m_empty = true ;
364
405
}
365
406
366
407
array_view (PyObject *arr, bool contiguous = false ) : m_arr(NULL ), m_data(NULL )
@@ -377,6 +418,7 @@ class array_view : public detail::array_view_accessors<array_view, T, ND>
377
418
m_data = other.m_data ;
378
419
m_shape = other.m_shape ;
379
420
m_strides = other.m_strides ;
421
+ m_empty = other.m_empty ;
380
422
}
381
423
382
424
array_view (PyArrayObject *arr, char *data, npy_intp *shape, npy_intp *strides)
@@ -386,6 +428,12 @@ class array_view : public detail::array_view_accessors<array_view, T, ND>
386
428
m_data = data;
387
429
m_shape = shape;
388
430
m_strides = strides;
431
+ m_empty = (ND == 0 );
432
+ for (size_t i = 0 ; i < ND; i++) {
433
+ if (shape[i] == 0 ) {
434
+ m_empty = true ;
435
+ }
436
+ }
389
437
}
390
438
391
439
array_view (npy_intp shape[ND]) : m_arr(NULL ), m_shape(NULL ), m_strides(NULL ), m_data(NULL )
@@ -416,6 +464,7 @@ class array_view : public detail::array_view_accessors<array_view, T, ND>
416
464
m_data = other.m_data ;
417
465
m_shape = other.m_shape ;
418
466
m_strides = other.m_strides ;
467
+ m_empty = other.m_empty ;
419
468
}
420
469
return *this ;
421
470
}
@@ -430,6 +479,7 @@ class array_view : public detail::array_view_accessors<array_view, T, ND>
430
479
m_data = NULL ;
431
480
m_shape = zeros;
432
481
m_strides = zeros;
482
+ m_empty = true ;
433
483
} else {
434
484
if (contiguous) {
435
485
tmp = (PyArrayObject *)PyArray_ContiguousFromAny (arr, type_num_of<T>::value, 0 , ND);
@@ -446,10 +496,11 @@ class array_view : public detail::array_view_accessors<array_view, T, ND>
446
496
m_data = NULL ;
447
497
m_shape = zeros;
448
498
m_strides = zeros;
449
- if (PyArray_NDIM (tmp) == 0 && ND == 0 ) {
450
- m_arr = tmp;
451
- return 1 ;
452
- }
499
+ if (PyArray_NDIM (tmp) == 0 && ND == 0 ) {
500
+ m_arr = tmp;
501
+ m_empty = true ;
502
+ return 1 ;
503
+ }
453
504
}
454
505
if (PyArray_NDIM (tmp) != ND) {
455
506
PyErr_Format (PyExc_ValueError,
@@ -466,6 +517,12 @@ class array_view : public detail::array_view_accessors<array_view, T, ND>
466
517
m_shape = PyArray_DIMS (m_arr);
467
518
m_strides = PyArray_STRIDES (m_arr);
468
519
m_data = (char *)PyArray_BYTES (tmp);
520
+ m_empty = (ND == 0 );
521
+ for (size_t i = 0 ; i < ND; i++) {
522
+ if (m_shape[i] == 0 ) {
523
+ m_empty = true ;
524
+ }
525
+ }
469
526
}
470
527
471
528
return 1 ;
0 commit comments