xnd 0.2.0dev6 → 0.2.0dev7

Sign up to get free protection for your applications and to get access to all the features.
Files changed (74) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +2 -0
  3. data/Rakefile +1 -1
  4. data/ext/ruby_xnd/GPATH +0 -0
  5. data/ext/ruby_xnd/GRTAGS +0 -0
  6. data/ext/ruby_xnd/GTAGS +0 -0
  7. data/ext/ruby_xnd/extconf.rb +8 -5
  8. data/ext/ruby_xnd/gc_guard.c +53 -2
  9. data/ext/ruby_xnd/gc_guard.h +8 -2
  10. data/ext/ruby_xnd/include/overflow.h +147 -0
  11. data/ext/ruby_xnd/include/ruby_xnd.h +62 -0
  12. data/ext/ruby_xnd/include/xnd.h +590 -0
  13. data/ext/ruby_xnd/lib/libxnd.a +0 -0
  14. data/ext/ruby_xnd/lib/libxnd.so +1 -0
  15. data/ext/ruby_xnd/lib/libxnd.so.0 +1 -0
  16. data/ext/ruby_xnd/lib/libxnd.so.0.2.0dev3 +0 -0
  17. data/ext/ruby_xnd/ruby_xnd.c +556 -47
  18. data/ext/ruby_xnd/ruby_xnd.h +2 -1
  19. data/ext/ruby_xnd/xnd/Makefile +80 -0
  20. data/ext/ruby_xnd/xnd/config.h +26 -0
  21. data/ext/ruby_xnd/xnd/config.h.in +3 -0
  22. data/ext/ruby_xnd/xnd/config.log +421 -0
  23. data/ext/ruby_xnd/xnd/config.status +1023 -0
  24. data/ext/ruby_xnd/xnd/configure +376 -8
  25. data/ext/ruby_xnd/xnd/configure.ac +48 -7
  26. data/ext/ruby_xnd/xnd/doc/xnd/index.rst +3 -1
  27. data/ext/ruby_xnd/xnd/doc/xnd/{types.rst → xnd.rst} +3 -18
  28. data/ext/ruby_xnd/xnd/libxnd/Makefile +142 -0
  29. data/ext/ruby_xnd/xnd/libxnd/Makefile.in +43 -3
  30. data/ext/ruby_xnd/xnd/libxnd/Makefile.vc +19 -3
  31. data/ext/ruby_xnd/xnd/libxnd/bitmaps.c +42 -3
  32. data/ext/ruby_xnd/xnd/libxnd/bitmaps.o +0 -0
  33. data/ext/ruby_xnd/xnd/libxnd/bounds.c +366 -0
  34. data/ext/ruby_xnd/xnd/libxnd/bounds.o +0 -0
  35. data/ext/ruby_xnd/xnd/libxnd/contrib.h +98 -0
  36. data/ext/ruby_xnd/xnd/libxnd/contrib/bfloat16.h +213 -0
  37. data/ext/ruby_xnd/xnd/libxnd/copy.c +155 -4
  38. data/ext/ruby_xnd/xnd/libxnd/copy.o +0 -0
  39. data/ext/ruby_xnd/xnd/libxnd/cuda/cuda_memory.cu +121 -0
  40. data/ext/ruby_xnd/xnd/libxnd/cuda/cuda_memory.h +58 -0
  41. data/ext/ruby_xnd/xnd/libxnd/equal.c +195 -7
  42. data/ext/ruby_xnd/xnd/libxnd/equal.o +0 -0
  43. data/ext/ruby_xnd/xnd/libxnd/inline.h +32 -0
  44. data/ext/ruby_xnd/xnd/libxnd/libxnd.a +0 -0
  45. data/ext/ruby_xnd/xnd/libxnd/libxnd.so +1 -0
  46. data/ext/ruby_xnd/xnd/libxnd/libxnd.so.0 +1 -0
  47. data/ext/ruby_xnd/xnd/libxnd/libxnd.so.0.2.0dev3 +0 -0
  48. data/ext/ruby_xnd/xnd/libxnd/shape.c +207 -0
  49. data/ext/ruby_xnd/xnd/libxnd/shape.o +0 -0
  50. data/ext/ruby_xnd/xnd/libxnd/split.c +2 -2
  51. data/ext/ruby_xnd/xnd/libxnd/split.o +0 -0
  52. data/ext/ruby_xnd/xnd/libxnd/tests/Makefile +39 -0
  53. data/ext/ruby_xnd/xnd/libxnd/xnd.c +613 -91
  54. data/ext/ruby_xnd/xnd/libxnd/xnd.h +145 -4
  55. data/ext/ruby_xnd/xnd/libxnd/xnd.o +0 -0
  56. data/ext/ruby_xnd/xnd/python/test_xnd.py +1125 -50
  57. data/ext/ruby_xnd/xnd/python/xnd/__init__.py +609 -124
  58. data/ext/ruby_xnd/xnd/python/xnd/_version.py +1 -0
  59. data/ext/ruby_xnd/xnd/python/xnd/_xnd.c +1652 -101
  60. data/ext/ruby_xnd/xnd/python/xnd/libxnd.a +0 -0
  61. data/ext/ruby_xnd/xnd/python/xnd/libxnd.so +1 -0
  62. data/ext/ruby_xnd/xnd/python/xnd/libxnd.so.0 +1 -0
  63. data/ext/ruby_xnd/xnd/python/xnd/libxnd.so.0.2.0dev3 +0 -0
  64. data/ext/ruby_xnd/xnd/python/xnd/pyxnd.h +1 -1
  65. data/ext/ruby_xnd/xnd/python/xnd/util.h +25 -0
  66. data/ext/ruby_xnd/xnd/python/xnd/xnd.h +590 -0
  67. data/ext/ruby_xnd/xnd/python/xnd_randvalue.py +106 -6
  68. data/ext/ruby_xnd/xnd/python/xnd_support.py +4 -0
  69. data/ext/ruby_xnd/xnd/setup.py +46 -4
  70. data/lib/ruby_xnd.so +0 -0
  71. data/lib/xnd.rb +39 -3
  72. data/lib/xnd/version.rb +2 -2
  73. data/xnd.gemspec +2 -1
  74. metadata +58 -5
@@ -0,0 +1 @@
1
+ __version__ = '0.2.0dev3'
@@ -41,6 +41,7 @@
41
41
  #include "pyndtypes.h"
42
42
  #include "xnd.h"
43
43
  #include "util.h"
44
+ #include "overflow.h"
44
45
  #include "docstrings.h"
45
46
 
46
47
  #define XND_MODULE
@@ -77,6 +78,13 @@ seterr_int(ndt_context_t *ctx)
77
78
  return -1;
78
79
  }
79
80
 
81
+ static const ndt_t *
82
+ seterr_ndt(ndt_context_t *ctx)
83
+ {
84
+ (void)Ndt_SetError(ctx);
85
+ return NULL;
86
+ }
87
+
80
88
 
81
89
  /****************************************************************************/
82
90
  /* Singletons */
@@ -162,7 +170,7 @@ mblock_dealloc(MemoryBlockObject *self)
162
170
  }
163
171
 
164
172
  static MemoryBlockObject *
165
- mblock_empty(PyObject *type)
173
+ mblock_empty(PyObject *type, uint32_t flags)
166
174
  {
167
175
  NDT_STATIC_CONTEXT(ctx);
168
176
  MemoryBlockObject *self;
@@ -177,7 +185,7 @@ mblock_empty(PyObject *type)
177
185
  return NULL;
178
186
  }
179
187
 
180
- self->xnd = xnd_empty_from_type(CONST_NDT(type), XND_OWN_EMBEDDED, &ctx);
188
+ self->xnd = xnd_empty_from_type(NDT(type), XND_OWN_EMBEDDED|flags, &ctx);
181
189
  if (self->xnd == NULL) {
182
190
  Py_DECREF(self);
183
191
  return (MemoryBlockObject *)seterr(&ctx);
@@ -189,11 +197,11 @@ mblock_empty(PyObject *type)
189
197
  }
190
198
 
191
199
  static MemoryBlockObject *
192
- mblock_from_typed_value(PyObject *type, PyObject *value)
200
+ mblock_from_typed_value(PyObject *type, PyObject *value, uint32_t flags)
193
201
  {
194
202
  MemoryBlockObject *self;
195
203
 
196
- self = mblock_empty(type);
204
+ self = mblock_empty(type, flags);
197
205
  if (self == NULL) {
198
206
  return NULL;
199
207
  }
@@ -219,7 +227,8 @@ mblock_from_xnd(xnd_t *src)
219
227
  return (MemoryBlockObject *)seterr(&ctx);
220
228
  }
221
229
 
222
- type = Ndt_FromType((ndt_t *)x->master.type);
230
+ type = Ndt_FromType(x->master.type);
231
+ ndt_decref(x->master.type);
223
232
  if (type == NULL) {
224
233
  xnd_del(x);
225
234
  return NULL;
@@ -242,7 +251,8 @@ static PyObject *
242
251
  type_from_buffer(const Py_buffer *view)
243
252
  {
244
253
  NDT_STATIC_CONTEXT(ctx);
245
- ndt_t *t, *type;
254
+ PyObject *ret;
255
+ const ndt_t *t, *type;
246
256
  int64_t shape, step;
247
257
  int64_t i;
248
258
 
@@ -269,20 +279,31 @@ type_from_buffer(const Py_buffer *view)
269
279
  if (ndt_itemsize(type) != view->itemsize) {
270
280
  PyErr_SetString(PyExc_RuntimeError,
271
281
  "mismatch between computed itemsize and buffer itemsize");
272
- ndt_del(type);
282
+ ndt_decref(type);
273
283
  return NULL;
274
284
  }
275
285
 
276
286
  for (i=view->ndim-1, t=type; i>=0; i--, type=t) {
277
287
  shape = view->shape[i];
288
+
278
289
  step = view->strides[i] / view->itemsize;
290
+ if (step * view->itemsize != view->strides[i]) {
291
+ PyErr_SetString(PyExc_NotImplementedError,
292
+ "strides supplied by exporter are not a multiple of itemsize");
293
+ ndt_decref(type);
294
+ return NULL;
295
+ }
296
+
279
297
  t = ndt_fixed_dim(type, shape, step, &ctx);
298
+ ndt_decref(type);
280
299
  if (t == NULL) {
281
300
  return seterr(&ctx);
282
301
  }
283
302
  }
284
303
 
285
- return Ndt_FromType(t);
304
+ ret = Ndt_FromType(t);
305
+ ndt_decref(t);
306
+ return ret;
286
307
  }
287
308
 
288
309
  static MemoryBlockObject *
@@ -306,15 +327,6 @@ mblock_from_buffer(PyObject *obj)
306
327
  return NULL;
307
328
  }
308
329
 
309
- if (!PyBuffer_IsContiguous(self->view, 'A')) {
310
- /* Conversion from buf+strides to steps+linear_index is not possible
311
- if the start of the original data is missing. */
312
- PyErr_SetString(PyExc_NotImplementedError,
313
- "conversion from non-contiguous buffers is not implemented");
314
- Py_DECREF(self);
315
- return NULL;
316
- }
317
-
318
330
  self->type = type_from_buffer(self->view);
319
331
  if (self->type == NULL) {
320
332
  Py_DECREF(self);
@@ -332,16 +344,19 @@ mblock_from_buffer(PyObject *obj)
332
344
  self->xnd->master.bitmap.size = 0;
333
345
  self->xnd->master.bitmap.next = NULL;
334
346
  self->xnd->master.index = 0;
335
- self->xnd->master.type = CONST_NDT(self->type);
347
+ self->xnd->master.type = NDT(self->type);
336
348
  self->xnd->master.ptr = self->view->buf;
337
349
 
338
350
  return self;
339
351
  }
340
352
 
341
353
  static MemoryBlockObject *
342
- mblock_from_buffer_and_type(PyObject *obj, PyObject *type)
354
+ mblock_from_buffer_and_type(PyObject *obj, PyObject *type, int64_t linear_index,
355
+ int64_t bufsize)
343
356
  {
357
+ NDT_STATIC_CONTEXT(ctx);
344
358
  MemoryBlockObject *self;
359
+ const ndt_t *t;
345
360
 
346
361
  if (!Ndt_Check(type)) {
347
362
  PyErr_SetString(PyExc_TypeError, "expected ndt object");
@@ -359,20 +374,27 @@ mblock_from_buffer_and_type(PyObject *obj, PyObject *type)
359
374
  return NULL;
360
375
  }
361
376
 
362
- if (PyObject_GetBuffer(obj, self->view, PyBUF_FULL_RO) < 0) {
377
+ if (PyObject_GetBuffer(obj, self->view, PyBUF_SIMPLE) < 0) {
363
378
  Py_DECREF(self);
364
379
  return NULL;
365
380
  }
366
381
 
367
- if (!PyBuffer_IsContiguous(self->view, 'A')) {
368
- /* Conversion from buf+strides to steps+linear_index is not possible
369
- if the start of the original data is missing. */
370
- PyErr_SetString(PyExc_NotImplementedError,
371
- "conversion from non-contiguous buffers is not implemented");
382
+ if (self->view->readonly) {
383
+ PyErr_SetString(PyExc_ValueError, "buffer is readonly");
372
384
  Py_DECREF(self);
373
385
  return NULL;
374
386
  }
375
387
 
388
+ if (bufsize < 0) {
389
+ bufsize = self->view->len;
390
+ }
391
+
392
+ t = NDT(type);
393
+ if (xnd_bounds_check(t, linear_index, bufsize, &ctx) < 0) {
394
+ Py_DECREF(self);
395
+ return (MemoryBlockObject *)seterr(&ctx);
396
+ }
397
+
376
398
  Py_INCREF(type);
377
399
  self->type = type;
378
400
 
@@ -386,8 +408,8 @@ mblock_from_buffer_and_type(PyObject *obj, PyObject *type)
386
408
  self->xnd->master.bitmap.data = NULL;
387
409
  self->xnd->master.bitmap.size = 0;
388
410
  self->xnd->master.bitmap.next = NULL;
389
- self->xnd->master.index = 0;
390
- self->xnd->master.type = CONST_NDT(self->type);
411
+ self->xnd->master.index = linear_index;
412
+ self->xnd->master.type = t;
391
413
  self->xnd->master.ptr = self->view->buf;
392
414
 
393
415
  return self;
@@ -508,6 +530,47 @@ get_uint(PyObject *v, uint64_t max)
508
530
  return x;
509
531
  }
510
532
 
533
+ static int
534
+ union_tag_and_value_from_tuple(uint8_t *tag, PyObject **value, const ndt_t *t, PyObject *tuple)
535
+ {
536
+ PyObject *name;
537
+ int64_t i;
538
+
539
+ assert(t->tag == Union);
540
+ assert(PyTuple_Check(tuple));
541
+
542
+ if (PyTuple_GET_SIZE(tuple) != 2) {
543
+ PyErr_SetString(PyExc_ValueError,
544
+ "unions are represented by a tuple (tag, value), "
545
+ "where 'tag' is a string");
546
+ return -1;
547
+ }
548
+
549
+ name = PyTuple_GET_ITEM(tuple, 0);
550
+ if (!PyUnicode_Check(name)) {
551
+ PyErr_SetString(PyExc_TypeError,
552
+ "unions are represented by a tuple (tag, value), "
553
+ "where 'tag' is a string");
554
+ return -1;
555
+ }
556
+
557
+ for (i = 0; i < t->Union.ntags; i++) {
558
+ if (PyUnicode_CompareWithASCIIString(name, t->Union.tags[i]) == 0) {
559
+ break;
560
+ }
561
+ }
562
+
563
+ if (i == t->Union.ntags) {
564
+ PyErr_Format(PyExc_ValueError, "'%s' s not a valid tag", name);
565
+ return -1;
566
+ }
567
+
568
+ *tag = (uint8_t)i;
569
+ *value = PyTuple_GET_ITEM(tuple, 1);
570
+
571
+ return 0;
572
+ }
573
+
511
574
  static int
512
575
  mblock_init(xnd_t * const x, PyObject *v)
513
576
  {
@@ -597,6 +660,27 @@ mblock_init(xnd_t * const x, PyObject *v)
597
660
  return 0;
598
661
  }
599
662
 
663
+ case VarDimElem: {
664
+ int64_t start, step, shape;
665
+
666
+ shape = ndt_var_indices(&start, &step, t, x->index, &ctx);
667
+ if (shape < 0) {
668
+ return seterr_int(&ctx);
669
+ }
670
+
671
+ const int64_t i = adjust_index(t->VarDimElem.index, shape, &ctx);
672
+ if (i < 0) {
673
+ return seterr_int(&ctx);
674
+ }
675
+
676
+ xnd_t next = xnd_var_dim_next(x, start, step, i);
677
+ if (mblock_init(&next, v) < 0) {
678
+ return -1;
679
+ }
680
+
681
+ return 0;
682
+ }
683
+
600
684
  case Tuple: {
601
685
  const int64_t shape = t->Tuple.shape;
602
686
  int64_t i;
@@ -670,6 +754,32 @@ mblock_init(xnd_t * const x, PyObject *v)
670
754
  return 0;
671
755
  }
672
756
 
757
+ case Union: {
758
+ PyObject *tmp;
759
+ uint8_t tag;
760
+
761
+ if (!PyTuple_Check(v)) {
762
+ PyErr_Format(PyExc_TypeError,
763
+ "xnd: expected tuple, not '%.200s'", Py_TYPE(v)->tp_name);
764
+ return -1;
765
+ }
766
+
767
+ if (union_tag_and_value_from_tuple(&tag, &tmp, t, v) < 0) {
768
+ return -1;
769
+ }
770
+
771
+ xnd_clear(x, XND_OWN_EMBEDDED);
772
+ XND_UNION_TAG(x->ptr) = tag;
773
+
774
+ xnd_t next = xnd_union_next(x, &ctx);
775
+ if (next.ptr == NULL) {
776
+ Py_DECREF(tmp);
777
+ return seterr_int(&ctx);
778
+ }
779
+
780
+ return mblock_init(&next, tmp);
781
+ }
782
+
673
783
  case Ref: {
674
784
  xnd_t next = xnd_ref_next(x, &ctx);
675
785
  if (next.ptr == NULL) {
@@ -806,6 +916,15 @@ mblock_init(xnd_t * const x, PyObject *v)
806
916
  return 0;
807
917
  }
808
918
 
919
+ case BFloat16: {
920
+ double tmp = PyFloat_AsDouble(v);
921
+ if (tmp == -1 && PyErr_Occurred()) {
922
+ return -1;
923
+ }
924
+ xnd_bfloat_pack(x->ptr, tmp);
925
+ return 0;
926
+ }
927
+
809
928
  case Float16: {
810
929
  #if PY_VERSION_HEX >= 0x03060000
811
930
  double tmp = PyFloat_AsDouble(v);
@@ -836,6 +955,16 @@ mblock_init(xnd_t * const x, PyObject *v)
836
955
  return _PyFloat_Pack8(tmp, (unsigned char *)x->ptr, le(t->flags));
837
956
  }
838
957
 
958
+ case BComplex32: {
959
+ Py_complex c = PyComplex_AsCComplex(v);
960
+ if (c.real == -1.0 && PyErr_Occurred()) {
961
+ return -1;
962
+ }
963
+ xnd_bfloat_pack(x->ptr, c.real);
964
+ xnd_bfloat_pack(x->ptr+2, c.imag);
965
+ return 0;
966
+ }
967
+
839
968
  case Complex32: {
840
969
  #if PY_VERSION_HEX >= 0x03060000
841
970
  Py_complex c = PyComplex_AsCComplex(v);
@@ -1054,6 +1183,43 @@ mblock_init(xnd_t * const x, PyObject *v)
1054
1183
  return 0;
1055
1184
  }
1056
1185
 
1186
+ case Array: {
1187
+ bool overflow = false;
1188
+
1189
+ if (!PyList_Check(v)) {
1190
+ PyErr_Format(PyExc_TypeError,
1191
+ "xnd: expected list, not '%.200s'", Py_TYPE(v)->tp_name);
1192
+ return -1;
1193
+ }
1194
+
1195
+ const Py_ssize_t shape = PyList_GET_SIZE(v);
1196
+ const int64_t size = MULi64(shape, t->Array.itemsize, &overflow);
1197
+ if (overflow) {
1198
+ ndt_err_format(&ctx, NDT_ValueError,
1199
+ "datasize of flexible array is too large");
1200
+ return seterr_int(&ctx);
1201
+ }
1202
+
1203
+ char *data = ndt_aligned_calloc(t->align, size);
1204
+ if (data == NULL) {
1205
+ PyErr_NoMemory();
1206
+ return -1;
1207
+ }
1208
+
1209
+ xnd_clear(x, XND_OWN_EMBEDDED);
1210
+ XND_ARRAY_SHAPE(x->ptr) = shape;
1211
+ XND_ARRAY_DATA(x->ptr) = data;
1212
+
1213
+ for (int64_t i = 0; i < shape; i++) {
1214
+ xnd_t next = xnd_array_next(x, i);
1215
+ if (mblock_init(&next, PyList_GET_ITEM(v, i)) < 0) {
1216
+ return -1;
1217
+ }
1218
+ }
1219
+
1220
+ return 0;
1221
+ }
1222
+
1057
1223
  case Categorical: {
1058
1224
  int64_t k;
1059
1225
 
@@ -1225,20 +1391,59 @@ pyxnd_from_mblock(PyTypeObject *tp, MemoryBlockObject *mblock)
1225
1391
  return (PyObject *)self;
1226
1392
  }
1227
1393
 
1394
+ static uint32_t
1395
+ device_flags(PyObject *tuple)
1396
+ {
1397
+ PyObject *device;
1398
+ PyObject *no;
1399
+
1400
+ if (!PyTuple_Check(tuple) || PyTuple_GET_SIZE(tuple) != 2) {
1401
+ PyErr_SetString(PyExc_TypeError,
1402
+ "device argument must be of the form (device_name, device_no)");
1403
+ return UINT32_MAX;
1404
+ }
1405
+
1406
+ device = PyTuple_GET_ITEM(tuple, 0);
1407
+ if (!PyUnicode_Check(device) ||
1408
+ PyUnicode_CompareWithASCIIString(device, "cuda") != 0) {
1409
+ PyErr_SetString(PyExc_ValueError,
1410
+ "currently only 'cuda' is supported as a device name");
1411
+ return UINT32_MAX;
1412
+ }
1413
+
1414
+ no = PyTuple_GET_ITEM(tuple, 1);
1415
+ if (!PyLong_Check(no) || PyLong_AsLong(no) != -1) {
1416
+ PyErr_SetString(PyExc_ValueError,
1417
+ "currently only 'cuda:managed' is supported as a device");
1418
+ return UINT32_MAX;
1419
+ }
1420
+
1421
+ return XND_CUDA_MANAGED;
1422
+ }
1423
+
1228
1424
  static PyObject *
1229
1425
  pyxnd_new(PyTypeObject *tp, PyObject *args, PyObject *kwds)
1230
1426
  {
1231
- static char *kwlist[] = {"type", "value", NULL};
1427
+ static char *kwlist[] = {"type", "value", "device", NULL};
1232
1428
  PyObject *type = NULL;
1233
1429
  PyObject *value = NULL;
1430
+ PyObject *tuple = Py_None;
1234
1431
  MemoryBlockObject *mblock;
1432
+ uint32_t flags = 0;
1235
1433
 
1236
- if (!PyArg_ParseTupleAndKeywords(args, kwds, "OO", kwlist, &type,
1237
- &value)) {
1434
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "OO|O", kwlist, &type,
1435
+ &value, &tuple)) {
1238
1436
  return NULL;
1239
1437
  }
1240
1438
 
1241
- mblock = mblock_from_typed_value(type, value);
1439
+ if (tuple != Py_None) {
1440
+ flags = device_flags(tuple);
1441
+ if (flags == UINT32_MAX) {
1442
+ return NULL;
1443
+ }
1444
+ }
1445
+
1446
+ mblock = mblock_from_typed_value(type, value, flags);
1242
1447
  if (mblock == NULL) {
1243
1448
  return NULL;
1244
1449
  }
@@ -1247,16 +1452,32 @@ pyxnd_new(PyTypeObject *tp, PyObject *args, PyObject *kwds)
1247
1452
  }
1248
1453
 
1249
1454
  static PyObject *
1250
- pyxnd_empty(PyTypeObject *tp, PyObject *type)
1455
+ pyxnd_empty(PyTypeObject *tp, PyObject *args, PyObject *kwds)
1251
1456
  {
1457
+ static char *kwlist[] = {"type", "device", NULL};
1458
+ PyObject *type = Py_None;
1459
+ PyObject *tuple = Py_None;
1252
1460
  MemoryBlockObject *mblock;
1461
+ uint32_t flags = 0;
1462
+
1463
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|O", kwlist, &type,
1464
+ &tuple)) {
1465
+ return NULL;
1466
+ }
1467
+
1468
+ if (tuple != Py_None) {
1469
+ flags = device_flags(tuple);
1470
+ if (flags == UINT32_MAX) {
1471
+ return NULL;
1472
+ }
1473
+ }
1253
1474
 
1254
1475
  type = Ndt_FromObject(type);
1255
1476
  if (type == NULL) {
1256
1477
  return NULL;
1257
1478
  }
1258
1479
 
1259
- mblock = mblock_empty(type);
1480
+ mblock = mblock_empty(type, flags);
1260
1481
  Py_DECREF(type);
1261
1482
  if (mblock == NULL) {
1262
1483
  return NULL;
@@ -1290,7 +1511,7 @@ pyxnd_from_buffer_and_type(PyTypeObject *tp, PyObject *args, PyObject *kwds)
1290
1511
  return NULL;
1291
1512
  }
1292
1513
 
1293
- mblock = mblock_from_buffer_and_type(obj, type);
1514
+ mblock = mblock_from_buffer_and_type(obj, type, 0, -1);
1294
1515
  if (mblock == NULL) {
1295
1516
  return NULL;
1296
1517
  }
@@ -1402,6 +1623,23 @@ _pyxnd_value(const xnd_t * const x, const int64_t maxshape)
1402
1623
  return lst;
1403
1624
  }
1404
1625
 
1626
+ case VarDimElem: {
1627
+ int64_t start, step, shape;
1628
+
1629
+ shape = ndt_var_indices(&start, &step, t, x->index, &ctx);
1630
+ if (shape < 0) {
1631
+ return seterr(&ctx);
1632
+ }
1633
+
1634
+ const int64_t i = adjust_index(t->VarDimElem.index, shape, &ctx);
1635
+ if (i < 0) {
1636
+ return seterr(&ctx);
1637
+ }
1638
+
1639
+ const xnd_t next = xnd_var_dim_next(x, start, step, i);
1640
+ return _pyxnd_value(&next, maxshape);
1641
+ }
1642
+
1405
1643
  case Tuple: {
1406
1644
  PyObject *tuple, *v;
1407
1645
  int64_t shape, i;
@@ -1485,6 +1723,37 @@ _pyxnd_value(const xnd_t * const x, const int64_t maxshape)
1485
1723
  return dict;
1486
1724
  }
1487
1725
 
1726
+ case Union: {
1727
+ PyObject *tuple, *tag, *v;
1728
+
1729
+ tuple = PyTuple_New(2);
1730
+ if (tuple == NULL) {
1731
+ return NULL;
1732
+ }
1733
+
1734
+ const uint8_t i = XND_UNION_TAG(x->ptr);
1735
+ tag = PyUnicode_FromString(t->Union.tags[i]);
1736
+ if (tag == NULL) {
1737
+ Py_DECREF(tuple);
1738
+ return NULL;
1739
+ }
1740
+ PyTuple_SET_ITEM(tuple, 0, tag);
1741
+
1742
+ const xnd_t next = xnd_union_next(x, &ctx);
1743
+ if (next.ptr == NULL) {
1744
+ return seterr(&ctx);
1745
+ }
1746
+
1747
+ v = _pyxnd_value(&next, maxshape);
1748
+ if (v == NULL) {
1749
+ Py_DECREF(tuple);
1750
+ return NULL;
1751
+ }
1752
+ PyTuple_SET_ITEM(tuple, 1, v);
1753
+
1754
+ return tuple;
1755
+ }
1756
+
1488
1757
  case Ref: {
1489
1758
  const xnd_t next = xnd_ref_next(x, &ctx);
1490
1759
  if (next.ptr == NULL) {
@@ -1570,6 +1839,11 @@ _pyxnd_value(const xnd_t * const x, const int64_t maxshape)
1570
1839
  return PyLong_FromUnsignedLongLong(tmp);
1571
1840
  }
1572
1841
 
1842
+ case BFloat16: {
1843
+ double tmp = xnd_bfloat_unpack(x->ptr);
1844
+ return PyFloat_FromDouble(tmp);
1845
+ }
1846
+
1573
1847
  case Float16: {
1574
1848
  #if PY_VERSION_HEX >= 0x03060000
1575
1849
  double tmp = _PyFloat_Unpack2((unsigned char *)x->ptr, le(t->flags));
@@ -1600,6 +1874,13 @@ _pyxnd_value(const xnd_t * const x, const int64_t maxshape)
1600
1874
  return PyFloat_FromDouble(tmp);
1601
1875
  }
1602
1876
 
1877
+ case BComplex32: {
1878
+ Py_complex c;
1879
+ c.real = xnd_bfloat_unpack(x->ptr);
1880
+ c.imag = xnd_bfloat_unpack(x->ptr+2);
1881
+ return PyComplex_FromCComplex(c);
1882
+ }
1883
+
1603
1884
  case Complex32: {
1604
1885
  #if PY_VERSION_HEX >= 0x03060000
1605
1886
  Py_complex c;
@@ -1681,8 +1962,8 @@ _pyxnd_value(const xnd_t * const x, const int64_t maxshape)
1681
1962
  }
1682
1963
 
1683
1964
  case String: {
1684
- const char *s = XND_POINTER_DATA(x->ptr);
1685
- Py_ssize_t size = s ? strlen(s) : 0;
1965
+ const char *s = XND_STRING_DATA(x->ptr);
1966
+ Py_ssize_t size = strlen(s);
1686
1967
 
1687
1968
  return PyUnicode_FromStringAndSize(s, size);
1688
1969
  }
@@ -1694,6 +1975,38 @@ _pyxnd_value(const xnd_t * const x, const int64_t maxshape)
1694
1975
  return bytes_from_string_and_size(s, size);
1695
1976
  }
1696
1977
 
1978
+ case Array: {
1979
+ PyObject *lst, *v;
1980
+ int64_t shape;
1981
+
1982
+ shape = XND_ARRAY_SHAPE(x->ptr);
1983
+ if (shape > maxshape) {
1984
+ shape = maxshape;
1985
+ }
1986
+
1987
+ lst = list_new(shape);
1988
+ if (lst == NULL) {
1989
+ return NULL;
1990
+ }
1991
+
1992
+ for (int64_t i = 0; i < shape; i++) {
1993
+ if (i == maxshape-1) {
1994
+ PyList_SET_ITEM(lst, i, xnd_ellipsis());
1995
+ break;
1996
+ }
1997
+
1998
+ const xnd_t next = xnd_array_next(x, i);
1999
+ v = _pyxnd_value(&next, maxshape);
2000
+ if (v == NULL) {
2001
+ Py_DECREF(lst);
2002
+ return NULL;
2003
+ }
2004
+ PyList_SET_ITEM(lst, i, v);
2005
+ }
2006
+
2007
+ return lst;
2008
+ }
2009
+
1697
2010
  case Categorical: {
1698
2011
  int64_t k;
1699
2012
 
@@ -1760,10 +2073,11 @@ pyxnd_view_move_type(const XndObject *src, xnd_t *x)
1760
2073
  XndObject *view;
1761
2074
  PyObject *type;
1762
2075
 
1763
- type = Ndt_MoveSubtree(src->type, (ndt_t *)x->type);
2076
+ type = Ndt_FromType(x->type);
1764
2077
  if (type == NULL) {
1765
2078
  return NULL;
1766
2079
  }
2080
+ ndt_decref(x->type);
1767
2081
 
1768
2082
  view = pyxnd_alloc(Py_TYPE(src));
1769
2083
  if (view == NULL) {
@@ -1813,6 +2127,24 @@ pyxnd_len(const xnd_t *x)
1813
2127
  return safe_downcast(shape);
1814
2128
  }
1815
2129
 
2130
+ case VarDimElem: {
2131
+ NDT_STATIC_CONTEXT(ctx);
2132
+ int64_t start, step, shape;
2133
+
2134
+ shape = ndt_var_indices(&start, &step, t, x->index, &ctx);
2135
+ if (shape < 0) {
2136
+ return seterr_int(&ctx);
2137
+ }
2138
+
2139
+ const int64_t i = adjust_index(t->VarDimElem.index, shape, &ctx);
2140
+ if (i < 0) {
2141
+ return seterr_int(&ctx);
2142
+ }
2143
+
2144
+ const xnd_t next = xnd_var_dim_next(x, start, step, i);
2145
+ return pyxnd_len(&next);
2146
+ }
2147
+
1816
2148
  case Tuple: {
1817
2149
  return safe_downcast(t->Tuple.shape);
1818
2150
  }
@@ -1821,6 +2153,20 @@ pyxnd_len(const xnd_t *x)
1821
2153
  return safe_downcast(t->Record.shape);
1822
2154
  }
1823
2155
 
2156
+ case Array: {
2157
+ const int64_t shape = XND_ARRAY_SHAPE(x->ptr);
2158
+ return safe_downcast(shape);
2159
+ }
2160
+
2161
+ case Union: {
2162
+ const xnd_t next = xnd_union_next(x, &ctx);
2163
+ if (next.ptr == NULL) {
2164
+ return seterr_int(&ctx);
2165
+ }
2166
+
2167
+ return pyxnd_len(&next);
2168
+ }
2169
+
1824
2170
  case Ref: {
1825
2171
  const xnd_t next = xnd_ref_next(x, &ctx);
1826
2172
  if (next.ptr == NULL) {
@@ -1932,7 +2278,7 @@ convert_key(xnd_index_t *indices, int *len, PyObject *key)
1932
2278
  }
1933
2279
  }
1934
2280
 
1935
- *len = size;
2281
+ *len = (int)size;
1936
2282
  return flags;
1937
2283
  }
1938
2284
 
@@ -1962,36 +2308,99 @@ pyxnd_subscript(XndObject *self, PyObject *key)
1962
2308
  return pyxnd_view_move_type(self, &x);
1963
2309
  }
1964
2310
 
1965
- static void
1966
- free_slices(xnd_t *lst, int64_t start, int64_t stop)
1967
- {
1968
- for (int64_t i = start; i < stop; i++) {
1969
- ndt_del((ndt_t *)lst[i].type);
1970
- }
1971
-
1972
- ndt_free(lst);
1973
- }
1974
-
1975
2311
  static PyObject *
1976
- pyxnd_split(PyObject *self, PyObject *args, PyObject *kwds)
2312
+ pyxnd_reshape(PyObject *self, PyObject *args, PyObject *kwds)
1977
2313
  {
1978
- static char *kwlist[] = {"n", "max_outer", NULL};
2314
+ static char *kwlist[] = {"shape", "order", NULL};
1979
2315
  NDT_STATIC_CONTEXT(ctx);
1980
- PyObject *max = Py_None;
1981
- PyObject *nparts;
1982
- int max_outer = NDT_MAX_DIM;
1983
- PyObject *res;
1984
- xnd_t *slices;
1985
- int64_t n;
1986
-
1987
- if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|O", kwlist, &nparts, &max)) {
2316
+ PyObject *tuple = NULL;
2317
+ PyObject *order = Py_None;
2318
+ int64_t shape[NDT_MAX_DIM];
2319
+ char ord = 'C';
2320
+ Py_ssize_t n;
2321
+
2322
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|O", kwlist, &tuple,
2323
+ &order)) {
1988
2324
  return NULL;
1989
2325
  }
1990
2326
 
1991
- n = PyLong_AsLongLong(nparts);
1992
- if (n == -1 && PyErr_Occurred()) {
1993
- return NULL;
1994
- }
2327
+ if (order != Py_None) {
2328
+ const char *c = PyUnicode_AsUTF8(order);
2329
+ if (strlen(c) != 1) {
2330
+ PyErr_SetString(PyExc_TypeError,
2331
+ "'order' argument must be a 'C', 'F' or 'A'");
2332
+ return NULL;
2333
+ }
2334
+ ord = c[0];
2335
+ }
2336
+
2337
+ if (!PyTuple_Check(tuple)) {
2338
+ PyErr_SetString(PyExc_TypeError,
2339
+ "'shape' argument must be a tuple");
2340
+ return NULL;
2341
+ }
2342
+
2343
+ n = PyTuple_GET_SIZE(tuple);
2344
+ if (n > NDT_MAX_DIM) {
2345
+ PyErr_SetString(PyExc_ValueError, "too many dimensions");
2346
+ return NULL;
2347
+ }
2348
+
2349
+ for (int i = 0; i < n; i++) {
2350
+ shape[i] = PyLong_AsLongLong(PyTuple_GET_ITEM(tuple, i));
2351
+ if (shape[i] < 0) {
2352
+ if (PyErr_Occurred()) {
2353
+ return NULL;
2354
+ }
2355
+ PyErr_SetString(PyExc_ValueError, "negative dimension size");
2356
+ return NULL;
2357
+ }
2358
+ }
2359
+
2360
+ xnd_t view = xnd_reshape(XND(self), shape, (int)n, ord, &ctx);
2361
+ if (xnd_err_occurred(&view)) {
2362
+ return seterr(&ctx);
2363
+ }
2364
+
2365
+ return pyxnd_view_move_type((XndObject *)self, &view);
2366
+ }
2367
+
2368
+ static void
2369
+ free_slices(xnd_t *lst, int64_t start, int64_t stop)
2370
+ {
2371
+ for (int64_t i = start; i < stop; i++) {
2372
+ ndt_decref(lst[i].type);
2373
+ }
2374
+
2375
+ ndt_free(lst);
2376
+ }
2377
+
2378
+ static PyObject *
2379
+ pyxnd_split(PyObject *self, PyObject *args, PyObject *kwds)
2380
+ {
2381
+ static char *kwlist[] = {"n", "max_outer", NULL};
2382
+ NDT_STATIC_CONTEXT(ctx);
2383
+ PyObject *max = Py_None;
2384
+ PyObject *nparts;
2385
+ int max_outer = NDT_MAX_DIM;
2386
+ PyObject *res;
2387
+ xnd_t *slices;
2388
+ int64_t n;
2389
+
2390
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|O", kwlist, &nparts, &max)) {
2391
+ return NULL;
2392
+ }
2393
+
2394
+ n = PyLong_AsLongLong(nparts);
2395
+ if (n == -1 && PyErr_Occurred()) {
2396
+ return NULL;
2397
+ }
2398
+
2399
+ if (n < 1 || n > INT32_MAX) {
2400
+ PyErr_SetString(PyExc_ValueError,
2401
+ "n must be in [1, INT32_MAX]");
2402
+ return NULL;
2403
+ }
1995
2404
 
1996
2405
  if (max != Py_None) {
1997
2406
  long l = PyLong_AsLong(max);
@@ -2011,7 +2420,7 @@ pyxnd_split(PyObject *self, PyObject *args, PyObject *kwds)
2011
2420
  return seterr(&ctx);
2012
2421
  }
2013
2422
 
2014
- res = PyList_New(n);
2423
+ res = PyList_New((Py_ssize_t)n);
2015
2424
  if (res == NULL) {
2016
2425
  free_slices(slices, 0, n);
2017
2426
  return NULL;
@@ -2038,7 +2447,6 @@ pyxnd_assign(XndObject *self, PyObject *key, PyObject *value)
2038
2447
  NDT_STATIC_CONTEXT(ctx);
2039
2448
  xnd_index_t indices[NDT_MAX_DIM];
2040
2449
  xnd_t x;
2041
- int free_type = 0;
2042
2450
  int ret, len;
2043
2451
  uint8_t flags;
2044
2452
 
@@ -2057,20 +2465,7 @@ pyxnd_assign(XndObject *self, PyObject *key, PyObject *value)
2057
2465
  return -1;
2058
2466
  }
2059
2467
 
2060
- if (flags & KEY_SLICE) {
2061
- x = xnd_multikey(&self->xnd, indices, len, &ctx);
2062
- if (x.ptr == NULL) {
2063
- return seterr_int(&ctx);
2064
- }
2065
- free_type = 1;
2066
- }
2067
- else {
2068
- x = xnd_subtree(&self->xnd, indices, len, &ctx);
2069
- if (x.ptr == NULL) {
2070
- return seterr_int(&ctx);
2071
- }
2072
- }
2073
-
2468
+ x = xnd_subscript(&self->xnd, indices, len, &ctx);
2074
2469
  if (x.ptr == NULL) {
2075
2470
  return seterr_int(&ctx);
2076
2471
  }
@@ -2085,10 +2480,7 @@ pyxnd_assign(XndObject *self, PyObject *key, PyObject *value)
2085
2480
  ret = mblock_init(&x, value);
2086
2481
  }
2087
2482
 
2088
- if (free_type) {
2089
- ndt_del((ndt_t *)x.type);
2090
- }
2091
-
2483
+ ndt_decref(x.type);
2092
2484
  return ret;
2093
2485
  }
2094
2486
 
@@ -2108,6 +2500,65 @@ pyxnd_item(XndObject *self, Py_ssize_t index)
2108
2500
  return res;
2109
2501
  }
2110
2502
 
2503
+ static PyObject *
2504
+ pyxnd_transpose(PyObject *self, PyObject *args, PyObject *kwds)
2505
+ {
2506
+ static char *kwlist[] = {"permute", NULL};
2507
+ NDT_STATIC_CONTEXT(ctx);
2508
+ PyObject *permute = Py_None;
2509
+ int p[NDT_MAX_ARGS];
2510
+ const ndt_t *t;
2511
+ xnd_t x;
2512
+
2513
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "|O", kwlist, &permute)) {
2514
+ return NULL;
2515
+ }
2516
+
2517
+ if (permute != Py_None) {
2518
+ if (!PyList_Check(permute) && !PyTuple_Check(permute)) {
2519
+ PyErr_SetString(PyExc_TypeError,
2520
+ "the 'permute' argument must be a list or a tuple");
2521
+ return NULL;
2522
+ }
2523
+
2524
+ const Py_ssize_t len = PySequence_Fast_GET_SIZE(permute);
2525
+
2526
+ if (len > NDT_MAX_ARGS) {
2527
+ PyErr_SetString(PyExc_ValueError, "permutation list too long");
2528
+ return NULL;
2529
+ }
2530
+
2531
+ for (int i = 0; i < len; i++) {
2532
+ int v = PyLong_AsLong(PySequence_Fast_GET_ITEM(permute, i));
2533
+ if (v == -1 && PyErr_Occurred()) {
2534
+ return NULL;
2535
+ }
2536
+
2537
+ if (v < 0 || v > INT_MAX) {
2538
+ PyErr_SetString(PyExc_ValueError,
2539
+ "permutation index out of bounds");
2540
+ return NULL;
2541
+ }
2542
+
2543
+ p[i] = (int)v;
2544
+ }
2545
+
2546
+ t = ndt_transpose(XND_TYPE(self), p, (int)len, &ctx);
2547
+ }
2548
+ else {
2549
+ t = ndt_transpose(XND_TYPE(self), NULL, 0, &ctx);
2550
+ }
2551
+
2552
+ if (t == NULL) {
2553
+ return seterr(&ctx);
2554
+ }
2555
+
2556
+ x = *XND(self);
2557
+ x.type = t;
2558
+
2559
+ return pyxnd_view_move_type((XndObject *)self, &x);
2560
+ }
2561
+
2111
2562
  static PyObject *
2112
2563
  pyxnd_short_value(PyObject *self, PyObject *args, PyObject *kwds)
2113
2564
  {
@@ -2165,6 +2616,13 @@ pyxnd_type(PyObject *self, PyObject *args UNUSED)
2165
2616
  return TYPE_OWNER(self);
2166
2617
  }
2167
2618
 
2619
+ static PyObject *
2620
+ pyxnd_dtype(PyObject *self, PyObject *args UNUSED)
2621
+ {
2622
+ const ndt_t *dtype = ndt_dtype(XND_TYPE(self));
2623
+ return Ndt_FromType(dtype);
2624
+ }
2625
+
2168
2626
  static PyObject *
2169
2627
  pyxnd_ndim(PyObject *self, PyObject *args UNUSED)
2170
2628
  {
@@ -2185,13 +2643,273 @@ pyxnd_align(PyObject *self, PyObject *args UNUSED)
2185
2643
  return PyLong_FromUnsignedLong(align);
2186
2644
  }
2187
2645
 
2646
+ static PyObject *
2647
+ pyxnd_device(XndObject *self, PyObject *args UNUSED)
2648
+ {
2649
+ uint32_t flags = self->mblock->xnd->flags;
2650
+
2651
+ if (flags & XND_CUDA_MANAGED) {
2652
+ return PyUnicode_FromString("cuda:managed");
2653
+ }
2654
+
2655
+ Py_RETURN_NONE;
2656
+ }
2657
+
2658
+ static PyObject *
2659
+ pyxnd_copy_contiguous(PyObject *self, PyObject *args, PyObject *kwargs)
2660
+ {
2661
+ static char *kwlist[] = {"dtype", NULL};
2662
+ NDT_STATIC_CONTEXT(ctx);
2663
+ XndObject *src = (XndObject *)self;
2664
+ PyObject *dtype = Py_None;
2665
+ PyObject *dest;
2666
+ const ndt_t *t;
2667
+
2668
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|O", kwlist, &dtype)) {
2669
+ return NULL;
2670
+ }
2671
+
2672
+ if (dtype != Py_None) {
2673
+ if (!Ndt_Check(dtype)) {
2674
+ PyErr_Format(PyExc_TypeError,
2675
+ "dtype argument must be 'ndt', got '%.200s'",
2676
+ Py_TYPE(dtype)->tp_name);
2677
+ return NULL;
2678
+ }
2679
+ t = ndt_copy_contiguous_dtype(XND_TYPE(src), NDT(dtype), XND_INDEX(src),
2680
+ &ctx);
2681
+ }
2682
+ else {
2683
+ t = ndt_copy_contiguous(XND_TYPE(src), XND_INDEX(src), &ctx);
2684
+ }
2685
+
2686
+ if (t == NULL) {
2687
+ return seterr(&ctx);
2688
+ }
2689
+
2690
+ dest = Xnd_EmptyFromType(Py_TYPE(src), t, 0);
2691
+ ndt_decref(t);
2692
+ if (dest == NULL) {
2693
+ return NULL;
2694
+ }
2695
+
2696
+ if (xnd_copy(XND(dest), XND(src), src->mblock->xnd->flags, &ctx) < 0) {
2697
+ Py_DECREF(dest);
2698
+ return seterr(&ctx);
2699
+ }
2700
+
2701
+ return dest;
2702
+ }
2703
+
2704
+ static PyObject *
2705
+ pyxnd_tobytes(PyObject *self, PyObject *args UNUSED)
2706
+ {
2707
+ NDT_STATIC_CONTEXT(ctx);
2708
+ XndObject *src = (XndObject *)self;
2709
+ const ndt_t *t = XND_TYPE(self);
2710
+ PyObject *b;
2711
+
2712
+ if (!ndt_is_pointer_free(t)) {
2713
+ PyErr_SetString(PyExc_NotImplementedError,
2714
+ "tobytes() is not implemented for memory blocks with pointers");
2715
+ return NULL;
2716
+ }
2717
+
2718
+ if (ndt_is_optional(t) || ndt_subtree_is_optional(t)) {
2719
+ PyErr_SetString(PyExc_NotImplementedError,
2720
+ "serializing bitmaps is not implemented");
2721
+ return NULL;
2722
+ }
2723
+
2724
+ if (!ndt_is_ndarray(t)) {
2725
+ PyErr_SetString(PyExc_NotImplementedError,
2726
+ "tobytes() is only implemented for ndarrays");
2727
+ return NULL;
2728
+ }
2729
+
2730
+ const bool contiguous = ndt_is_c_contiguous(t) || ndt_is_f_contiguous(t) ||
2731
+ ndt_is_var_contiguous(t);
2732
+
2733
+ if (contiguous) {
2734
+ ndt_incref(t);
2735
+ }
2736
+ else {
2737
+ t = ndt_copy_contiguous(XND_TYPE(src), XND_INDEX(src), &ctx);
2738
+ if (t == NULL) {
2739
+ return seterr(&ctx);
2740
+ }
2741
+ }
2742
+
2743
+ b = PyBytes_FromStringAndSize(NULL, t->datasize);
2744
+ if (b == NULL) {
2745
+ ndt_decref(t);
2746
+ return NULL;
2747
+ }
2748
+ char *cp = PyBytes_AS_STRING(b);
2749
+
2750
+
2751
+ if (contiguous) {
2752
+ char *ptr = XND(src)->ptr;
2753
+ if (t->ndim != 0) {
2754
+ ptr += XND_INDEX(src) * t->Concrete.FixedDim.itemsize;
2755
+ }
2756
+
2757
+ memcpy(cp, ptr, t->datasize);
2758
+ }
2759
+ else {
2760
+ xnd_t x = xnd_error;
2761
+ x.type = t;
2762
+ x.ptr = cp;
2763
+
2764
+ if (xnd_copy(&x, XND(src), src->mblock->xnd->flags, &ctx) < 0) {
2765
+ Py_DECREF(b);
2766
+ ndt_decref(t);
2767
+ return seterr(&ctx);
2768
+ }
2769
+ }
2770
+
2771
+ ndt_decref(t);
2772
+ return b;
2773
+ }
2774
+
2775
+ static PyObject *
2776
+ _serialize(XndObject *self)
2777
+ {
2778
+ NDT_STATIC_CONTEXT(ctx);
2779
+ bool overflow = false;
2780
+ const xnd_t *x = XND(self);
2781
+ const ndt_t *t = XND_TYPE(self);
2782
+ PyObject *result;
2783
+ char *cp, *s;
2784
+ int64_t tlen;
2785
+ int64_t size;
2786
+
2787
+ if (!ndt_is_pointer_free(t)) {
2788
+ PyErr_SetString(PyExc_NotImplementedError,
2789
+ "serializing memory blocks with pointers is not implemented");
2790
+ return NULL;
2791
+ }
2792
+
2793
+ if (ndt_is_optional(t) || ndt_subtree_is_optional(t)) {
2794
+ PyErr_SetString(PyExc_NotImplementedError,
2795
+ "serializing bitmaps is not implemented");
2796
+ return NULL;
2797
+ }
2798
+
2799
+ if (!ndt_is_c_contiguous(t) && !ndt_is_f_contiguous(t) &&
2800
+ !ndt_is_var_contiguous(t)) {
2801
+ PyErr_SetString(PyExc_NotImplementedError,
2802
+ "serializing non-contiguous memory blocks is not implemented");
2803
+ return NULL;
2804
+ }
2805
+
2806
+ tlen = ndt_serialize(&s, t, &ctx);
2807
+ if (tlen < 0) {
2808
+ return seterr(&ctx);
2809
+ }
2810
+
2811
+ size = ADDi64(t->datasize, tlen, &overflow);
2812
+ size = ADDi64(size, 8, &overflow);
2813
+ if (overflow) {
2814
+ PyErr_SetString(PyExc_OverflowError, "too large to serialize");
2815
+ ndt_free(s);
2816
+ return NULL;
2817
+ }
2818
+
2819
+ result = PyBytes_FromStringAndSize(NULL, size);
2820
+ cp = PyBytes_AS_STRING(result);
2821
+
2822
+ char *ptr = x->ptr;
2823
+ if (t->ndim != 0) {
2824
+ ptr = x->ptr + x->index * t->Concrete.FixedDim.itemsize;
2825
+ }
2826
+
2827
+ memcpy(cp, ptr, t->datasize); cp += t->datasize;
2828
+ memcpy(cp, s, tlen); cp += tlen;
2829
+ memcpy(cp, &t->datasize, 8);
2830
+ ndt_free(s);
2831
+
2832
+ return result;
2833
+ }
2834
+
2835
+ static PyObject *
2836
+ pyxnd_serialize(PyObject *self, PyObject *args UNUSED)
2837
+ {
2838
+ return _serialize((XndObject *)self);
2839
+ }
2840
+
2841
+ static PyObject *
2842
+ pyxnd_deserialize(PyTypeObject *tp, PyObject *v)
2843
+ {
2844
+ NDT_STATIC_CONTEXT(ctx);
2845
+ MemoryBlockObject *mblock;
2846
+ bool overflow = false;
2847
+ int64_t mblock_size;
2848
+
2849
+ if (!PyBytes_Check(v)) {
2850
+ PyErr_Format(PyExc_TypeError,
2851
+ "expected bytes object, not '%.200s'", Py_TYPE(v)->tp_name);
2852
+ return NULL;
2853
+ }
2854
+
2855
+ const int64_t size = PyBytes_GET_SIZE(v);
2856
+ if (size < 8) {
2857
+ goto invalid_format;
2858
+ }
2859
+
2860
+ const char *s = PyBytes_AS_STRING(v);
2861
+ memcpy(&mblock_size, s+size-8, 8);
2862
+ if (mblock_size < 0) {
2863
+ goto invalid_format;
2864
+ }
2865
+
2866
+ const int64_t tmp = ADDi64(mblock_size, 8, &overflow);
2867
+ const int64_t tlen = size-tmp;
2868
+ if (overflow || tlen < 0) {
2869
+ goto invalid_format;
2870
+ }
2871
+
2872
+ const ndt_t *t = ndt_deserialize(s+mblock_size, tlen, &ctx);
2873
+ if (t == NULL) {
2874
+ return seterr(&ctx);
2875
+ }
2876
+
2877
+ if (t->datasize != mblock_size) {
2878
+ goto invalid_format;
2879
+ }
2880
+
2881
+ PyObject *type = Ndt_FromType(t);
2882
+ ndt_decref(t);
2883
+ if (type == NULL) {
2884
+ return NULL;
2885
+ }
2886
+
2887
+ mblock = mblock_empty(type, XND_OWN_EMBEDDED);
2888
+ Py_DECREF(type);
2889
+ if (mblock == NULL) {
2890
+ return NULL;
2891
+ }
2892
+
2893
+ memcpy(mblock->xnd->master.ptr, s, mblock_size);
2894
+
2895
+ return pyxnd_from_mblock(tp, mblock);
2896
+
2897
+
2898
+ invalid_format:
2899
+ PyErr_SetString(PyExc_ValueError,
2900
+ "invalid format for xnd deserialization");
2901
+ return NULL;
2902
+ }
2903
+
2188
2904
 
2189
2905
  static PyGetSetDef pyxnd_getsets [] =
2190
2906
  {
2191
2907
  { "type", (getter)pyxnd_type, NULL, doc_type, NULL},
2908
+ { "dtype", (getter)pyxnd_dtype, NULL, NULL, NULL},
2192
2909
  { "value", (getter)pyxnd_value, NULL, doc_value, NULL},
2193
2910
  { "align", (getter)pyxnd_align, NULL, doc_align, NULL},
2194
2911
  { "ndim", (getter)pyxnd_ndim, NULL, doc_ndim, NULL},
2912
+ { "device", (getter)pyxnd_device, NULL, NULL, NULL},
2195
2913
  {NULL}
2196
2914
  };
2197
2915
 
@@ -2214,12 +2932,18 @@ static PyMethodDef pyxnd_methods [] =
2214
2932
  /* Methods */
2215
2933
  { "short_value", (PyCFunction)pyxnd_short_value, METH_VARARGS|METH_KEYWORDS, doc_short_value },
2216
2934
  { "strict_equal", (PyCFunction)pyxnd_strict_equal, METH_O, NULL },
2935
+ { "copy_contiguous", (PyCFunction)pyxnd_copy_contiguous, METH_VARARGS|METH_KEYWORDS, NULL },
2217
2936
  { "split", (PyCFunction)pyxnd_split, METH_VARARGS|METH_KEYWORDS, NULL },
2937
+ { "transpose", (PyCFunction)pyxnd_transpose, METH_VARARGS|METH_KEYWORDS, NULL },
2938
+ { "tobytes", (PyCFunction)pyxnd_tobytes, METH_NOARGS, NULL },
2939
+ { "_reshape", (PyCFunction)pyxnd_reshape, METH_VARARGS|METH_KEYWORDS, NULL },
2940
+ { "_serialize", (PyCFunction)pyxnd_serialize, METH_NOARGS, NULL },
2218
2941
 
2219
2942
  /* Class methods */
2220
- { "empty", (PyCFunction)pyxnd_empty, METH_O|METH_CLASS, doc_empty },
2943
+ { "empty", (PyCFunction)pyxnd_empty, METH_VARARGS|METH_KEYWORDS|METH_CLASS, doc_empty },
2221
2944
  { "from_buffer", (PyCFunction)pyxnd_from_buffer, METH_O|METH_CLASS, doc_from_buffer },
2222
- { "_unsafe_from_data", (PyCFunction)pyxnd_from_buffer_and_type, METH_VARARGS|METH_KEYWORDS|METH_CLASS, NULL },
2945
+ { "from_buffer_and_type", (PyCFunction)pyxnd_from_buffer_and_type, METH_VARARGS|METH_KEYWORDS|METH_CLASS, NULL },
2946
+ { "deserialize", (PyCFunction)pyxnd_deserialize, METH_O|METH_CLASS, NULL },
2223
2947
 
2224
2948
  { NULL, NULL, 1 }
2225
2949
  };
@@ -2455,32 +3179,856 @@ static PyTypeObject Xnd_Type =
2455
3179
 
2456
3180
 
2457
3181
  /****************************************************************************/
2458
- /* C-API */
3182
+ /* Type inference */
2459
3183
  /****************************************************************************/
2460
3184
 
2461
- static void **xnd_api[XND_MAX_API];
3185
+ /**********************************************************************/
3186
+ /* Extract data and shapes from a value (possibly a nested list) */
3187
+ /**********************************************************************/
2462
3188
 
3189
+ #undef max
2463
3190
  static int
2464
- Xnd_CheckExact(const PyObject *v)
3191
+ max(int x, int y)
2465
3192
  {
2466
- return Py_TYPE(v) == &Xnd_Type;
3193
+ return x >= y ? x : y;
2467
3194
  }
2468
3195
 
3196
+ #undef min
2469
3197
  static int
2470
- Xnd_Check(const PyObject *v)
3198
+ min(int x, int y)
2471
3199
  {
2472
- return PyObject_TypeCheck(v, &Xnd_Type);
3200
+ return x <= y ? x : y;
2473
3201
  }
2474
3202
 
2475
- static const xnd_t *
2476
- CONST_XND(const PyObject *v)
3203
+ #define XND_NONE 0x0001U
3204
+ #define XND_DATA 0x0002U
3205
+ #define XND_LIST 0x0004U
3206
+
3207
+ static inline int
3208
+ check_level(int level)
2477
3209
  {
2478
- assert(Xnd_Check(v));
2479
- return &((XndObject *)v)->xnd;
3210
+ if (level >= NDT_MAX_DIM) {
3211
+ PyErr_Format(PyExc_ValueError,
3212
+ "too many dimensions, max %d", NDT_MAX_DIM);
3213
+ return -1;
3214
+ }
3215
+
3216
+ return 0;
3217
+ }
3218
+
3219
+ static int
3220
+ search(int level, PyObject *v, PyObject *data, PyObject *acc[NDT_MAX_DIM],
3221
+ int *min_level, int *max_level)
3222
+ {
3223
+ PyObject *shape;
3224
+ PyObject *item;
3225
+ Py_ssize_t len, i;
3226
+ int next_level;
3227
+ int ret;
3228
+
3229
+ if (PyList_Check(v)) {
3230
+ if (check_level(level) < 0) {
3231
+ return -1;
3232
+ }
3233
+
3234
+ len = PyList_GET_SIZE(v);
3235
+ shape = PyLong_FromSsize_t(len);
3236
+ if (shape == NULL) {
3237
+ return -1;
3238
+ }
3239
+
3240
+ ret = PyList_Append(acc[level], shape);
3241
+ Py_DECREF(shape);
3242
+ if (ret < 0) {
3243
+ return -1;
3244
+ }
3245
+
3246
+ next_level = level + 1;
3247
+ *max_level = max(next_level, *max_level);
3248
+
3249
+ if (len == 0) {
3250
+ *min_level = min(next_level, *min_level);
3251
+ }
3252
+ else {
3253
+ uint32_t types = 0;
3254
+ for (i = 0; i < len; i++) {
3255
+ item = PyList_GET_ITEM(v, i);
3256
+ if (item == Py_None) {
3257
+ types |= XND_NONE;
3258
+ }
3259
+ else if (PyList_Check(item)) {
3260
+ types |= XND_LIST;
3261
+ }
3262
+ else {
3263
+ types |= XND_DATA;
3264
+ }
3265
+ }
3266
+
3267
+ if (!(types & XND_LIST)) {
3268
+ for (i = 0; i < len; i++) {
3269
+ item = PyList_GET_ITEM(v, i);
3270
+ if (PyList_Append(data, item) < 0) {
3271
+ return -1;
3272
+ }
3273
+ }
3274
+ *min_level = min(next_level, *min_level);
3275
+ }
3276
+ else if (!(types & XND_DATA)) {
3277
+ if (check_level(next_level) < 0) {
3278
+ return -1;
3279
+ }
3280
+
3281
+ for (i = 0; i < len; i++) {
3282
+ item = PyList_GET_ITEM(v, i);
3283
+ if (item == Py_None) {
3284
+ if (PyList_Append(acc[next_level], item) < 0) {
3285
+ return -1;
3286
+ }
3287
+ }
3288
+ else {
3289
+ if (search(next_level, item, data, acc,
3290
+ min_level, max_level) < 0) {
3291
+ return -1;
3292
+ }
3293
+ }
3294
+ }
3295
+ }
3296
+ else {
3297
+ PyErr_Format(PyExc_ValueError,
3298
+ "lists that contain both data and lists cannot be typed");
3299
+ return -1;
3300
+ }
3301
+ }
3302
+ }
3303
+ else {
3304
+ if (PyList_Append(data, v) < 0) {
3305
+ return -1;
3306
+ }
3307
+ *min_level = min(level, *min_level);
3308
+ }
3309
+
3310
+ return 0;
2480
3311
  }
2481
3312
 
2482
3313
  static PyObject *
2483
- Xnd_EmptyFromType(PyTypeObject *tp, ndt_t *t)
3314
+ data_shapes(PyObject *m UNUSED, PyObject *v)
3315
+ {
3316
+ PyObject *acc[NDT_MAX_DIM] = {NULL};
3317
+ PyObject *data = NULL;
3318
+ PyObject *shapes = NULL;
3319
+ PyObject *tuple = NULL;
3320
+ int min_level = NDT_MAX_DIM;
3321
+ int max_level = 0;
3322
+ int i, k;
3323
+
3324
+ data = PyList_New(0);
3325
+ if (data == NULL) {
3326
+ return NULL;
3327
+ }
3328
+
3329
+ for (i = 0; i < NDT_MAX_DIM; i++) {
3330
+ acc[i] = PyList_New(0);
3331
+ if (acc[i] == NULL) {
3332
+ goto error;
3333
+ }
3334
+ }
3335
+
3336
+ if (search(0, v, data, acc, &min_level, &max_level) < 0) {
3337
+ goto error;
3338
+ }
3339
+
3340
+ if (min_level != max_level) {
3341
+ PyErr_Format(PyExc_ValueError,
3342
+ "unbalanced nested list: min depth: %d max depth: %d",
3343
+ min_level, max_level);
3344
+ goto error;
3345
+ }
3346
+
3347
+ shapes = PyList_New(max_level);
3348
+ if (shapes == NULL) {
3349
+ goto error;
3350
+ }
3351
+
3352
+ for (i=0, k=max_level-1; i < max_level; i++, k--) {
3353
+ PyList_SET_ITEM(shapes, i, acc[k]);
3354
+ }
3355
+
3356
+ for (; i < NDT_MAX_DIM; i++) {
3357
+ Py_DECREF(acc[i]);
3358
+ }
3359
+
3360
+ tuple = PyTuple_New(2);
3361
+ if (tuple == NULL) {
3362
+ Py_DECREF(data);
3363
+ Py_DECREF(shapes);
3364
+ return NULL;
3365
+ }
3366
+ PyTuple_SET_ITEM(tuple, 0, data);
3367
+ PyTuple_SET_ITEM(tuple, 1, shapes);
3368
+
3369
+ return tuple;
3370
+
3371
+ error:
3372
+ Py_XDECREF(data);
3373
+ for (i = 0; i < NDT_MAX_DIM; i++) {
3374
+ Py_XDECREF(acc[i]);
3375
+ }
3376
+ return NULL;
3377
+ }
3378
+
3379
+
3380
+ /**********************************************************************/
3381
+ /* Construct fixed or var dimensions from a list of shape lists */
3382
+ /**********************************************************************/
3383
+
3384
+ static bool
3385
+ require_var(PyObject *lst)
3386
+ {
3387
+ PyObject *shapes;
3388
+ PyObject *v, *w;
3389
+ Py_ssize_t len;
3390
+ Py_ssize_t i, k;
3391
+
3392
+ assert(PyList_Check(lst));
3393
+
3394
+ for (i = 0; i < PyList_GET_SIZE(lst); i++) {
3395
+ shapes = PyList_GET_ITEM(lst, i);
3396
+ assert(PyList_Check(shapes));
3397
+
3398
+ len = PyList_GET_SIZE(shapes);
3399
+ if (len == 0) {
3400
+ continue;
3401
+ }
3402
+
3403
+ v = PyList_GET_ITEM(shapes, 0);
3404
+ if (v == Py_None) {
3405
+ return true;
3406
+ }
3407
+ assert(PyLong_Check(v));
3408
+
3409
+ for (k = 1; k < PyList_GET_SIZE(shapes); k++) {
3410
+ w = PyList_GET_ITEM(shapes, k);
3411
+ if (w == Py_None) {
3412
+ return true;
3413
+ }
3414
+ assert(PyLong_Check(w));
3415
+
3416
+ if (long_compare((PyLongObject *)v, (PyLongObject *)w) != 0) {
3417
+ return true;
3418
+ }
3419
+ }
3420
+ }
3421
+
3422
+ return false;
3423
+ }
3424
+
3425
+ static const ndt_t *
3426
+ fixed_from_shapes(PyObject *lst, const ndt_t *type)
3427
+ {
3428
+ NDT_STATIC_CONTEXT(ctx);
3429
+ PyObject *shapes;
3430
+ PyObject *v;
3431
+ Py_ssize_t len;
3432
+ Py_ssize_t shape;
3433
+ Py_ssize_t i;
3434
+ const ndt_t *t;
3435
+
3436
+ assert(PyList_Check(lst));
3437
+
3438
+ ndt_incref(type);
3439
+
3440
+ for (i=0, t=type; i < PyList_GET_SIZE(lst); i++, type=t) {
3441
+ shapes = PyList_GET_ITEM(lst, i);
3442
+ assert(PyList_Check(shapes));
3443
+
3444
+ len = PyList_GET_SIZE(shapes);
3445
+
3446
+ if (len == 0) {
3447
+ shape = 0;
3448
+ }
3449
+ else {
3450
+ v = PyList_GET_ITEM(shapes, 0);
3451
+ shape = PyLong_AsSsize_t(v);
3452
+ if (shape < 0) {
3453
+ ndt_decref(t);
3454
+ return NULL;
3455
+ }
3456
+ }
3457
+
3458
+ t = ndt_fixed_dim(type, shape, INT64_MAX, &ctx);
3459
+ ndt_decref(type);
3460
+ if (t == NULL) {
3461
+ return seterr_ndt(&ctx);
3462
+ }
3463
+ }
3464
+
3465
+ return t;
3466
+ }
3467
+
3468
+ static const ndt_t *
3469
+ var_from_shapes(PyObject *lst, const ndt_t *dtype)
3470
+ {
3471
+ NDT_STATIC_CONTEXT(ctx);
3472
+ bool overflow = false;
3473
+ const ndt_t *t;
3474
+ ndt_offsets_t *offsets;
3475
+ int32_t *ptr;
3476
+ int64_t sum;
3477
+ Py_ssize_t len, slen;
3478
+ Py_ssize_t shape;
3479
+ Py_ssize_t i, k;
3480
+ bool opt;
3481
+
3482
+ assert(PyList_Check(lst));
3483
+ len = PyList_GET_SIZE(lst);
3484
+
3485
+ ndt_incref(dtype);
3486
+
3487
+ for (i=0, t=dtype; i < len; i++, dtype=t) {
3488
+ PyObject *shapes = PyList_GET_ITEM(lst, i);
3489
+ assert(PyList_Check(shapes));
3490
+ slen = PyList_GET_SIZE(shapes);
3491
+
3492
+ if (slen+1 > INT32_MAX) {
3493
+ PyErr_SetString(PyExc_ValueError,
3494
+ "variable dimension is too large");
3495
+ return NULL;
3496
+ }
3497
+
3498
+ offsets = ndt_offsets_new((int32_t)(slen+1), &ctx);
3499
+ if (offsets == NULL) {
3500
+ return seterr_ndt(&ctx);
3501
+ }
3502
+
3503
+ ptr = (int32_t *)offsets->v;
3504
+ sum = 0;
3505
+ ptr[0] = 0;
3506
+ opt = false;
3507
+
3508
+ for (k = 0; k < slen; k++) {
3509
+ PyObject *v = PyList_GET_ITEM(shapes, k);
3510
+
3511
+ if (v == Py_None) {
3512
+ shape = 0;
3513
+ opt = true;
3514
+ }
3515
+ else {
3516
+ shape = PyLong_AsSsize_t(v);
3517
+ if (shape < 0) {
3518
+ ndt_decref_offsets(offsets);
3519
+ return NULL;
3520
+ }
3521
+ }
3522
+
3523
+ sum = ADDi64(sum, shape, &overflow);
3524
+ if (overflow || sum > INT32_MAX) {
3525
+ PyErr_SetString(PyExc_ValueError,
3526
+ "variable dimension is too large");
3527
+ ndt_decref_offsets(offsets);
3528
+ return NULL;
3529
+ }
3530
+
3531
+ ptr[k+1] = (int32_t)sum;
3532
+ }
3533
+
3534
+ t = ndt_var_dim(dtype, offsets, 0, NULL, opt, &ctx);
3535
+
3536
+ ndt_decref(dtype);
3537
+ ndt_decref_offsets(offsets);
3538
+
3539
+ if (t == NULL) {
3540
+ return seterr_ndt(&ctx);
3541
+ }
3542
+ }
3543
+
3544
+ return t;
3545
+ }
3546
+
3547
+
3548
+ /**********************************************************************/
3549
+ /* Infer the dtype from a flat list of data */
3550
+ /**********************************************************************/
3551
+
3552
+ static const ndt_t *typeof(PyObject *v, bool replace_any, bool shortcut);
3553
+
3554
+
3555
+ #define XND_BOOL 0x0001U
3556
+ #define XND_FLOAT64 0x0002U
3557
+ #define XND_COMPLEX128 0x0004U
3558
+ #define XND_INT64 0x0008U
3559
+ #define XND_STRING 0x0010U
3560
+ #define XND_BYTES 0x0020U
3561
+ #define XND_OTHER 0x0040U
3562
+
3563
+ static inline uint32_t
3564
+ fast_dtypes(bool *opt, const PyObject *data)
3565
+ {
3566
+ uint32_t dtypes = 0;
3567
+
3568
+ assert(PyList_Check(data));
3569
+
3570
+ for (Py_ssize_t i = 0; i < PyList_GET_SIZE(data); i++) {
3571
+ PyObject *v = PyList_GET_ITEM(data, i);
3572
+
3573
+ if (v == Py_None) {
3574
+ *opt = true;
3575
+ }
3576
+ else if (PyBool_Check(v)) {
3577
+ dtypes |= XND_BOOL;
3578
+ }
3579
+ else if (PyFloat_Check(v)) {
3580
+ dtypes |= XND_FLOAT64;
3581
+ }
3582
+ else if (PyComplex_Check(v)) {
3583
+ dtypes |= XND_COMPLEX128;
3584
+ }
3585
+ else if (PyLong_Check(v)) {
3586
+ dtypes |= XND_INT64;
3587
+ }
3588
+ else if (PyUnicode_Check(v)) {
3589
+ dtypes |= XND_STRING;
3590
+ }
3591
+ else if (PyBytes_Check(v)) {
3592
+ dtypes |= XND_BYTES;
3593
+ }
3594
+ else {
3595
+ dtypes |= XND_OTHER;
3596
+ }
3597
+ }
3598
+
3599
+ if (dtypes == 0) {
3600
+ dtypes |= XND_FLOAT64;
3601
+ }
3602
+
3603
+ return dtypes;
3604
+ }
3605
+
3606
+ static const ndt_t *
3607
+ unify_dtypes(const PyObject *data, bool shortcut)
3608
+ {
3609
+ NDT_STATIC_CONTEXT(ctx);
3610
+ PyObject *v;
3611
+ const ndt_t *dtype;
3612
+ const ndt_t *t, *u;
3613
+ Py_ssize_t i;
3614
+
3615
+ if (!PyList_Check(data) || PyList_GET_SIZE(data) == 0) {
3616
+ PyErr_Format(PyExc_RuntimeError,
3617
+ "internal error: unify_dtypes expects non-empty list");
3618
+ return NULL;
3619
+ }
3620
+
3621
+ v = PyList_GET_ITEM(data, 0);
3622
+ dtype = typeof(v, false, shortcut);
3623
+ if (dtype == NULL) {
3624
+ return NULL;
3625
+ }
3626
+
3627
+ for (i = 1; i < PyList_GET_SIZE(data); i++) {
3628
+ v = PyList_GET_ITEM(data, i);
3629
+ t = typeof(v, false, shortcut);
3630
+ if (t == NULL) {
3631
+ ndt_decref(dtype);
3632
+ return NULL;
3633
+ }
3634
+
3635
+ if (ndt_equal(t, dtype)) {
3636
+ ndt_decref(t);
3637
+ }
3638
+ else {
3639
+ u = ndt_unify(t, dtype, &ctx);
3640
+ ndt_decref(t);
3641
+ ndt_decref(dtype);
3642
+ if (u == NULL) {
3643
+ return seterr_ndt(&ctx);
3644
+ }
3645
+ dtype = u;
3646
+ }
3647
+
3648
+ if (shortcut && ndt_is_concrete(dtype)) {
3649
+ break;
3650
+ }
3651
+ }
3652
+
3653
+ if (ndt_is_abstract(dtype)) {
3654
+ const ndt_t *u = ndt_unify_replace_any(dtype, dtype, &ctx);
3655
+ ndt_decref(dtype);
3656
+ if (u == NULL) {
3657
+ return seterr_ndt(&ctx);
3658
+ }
3659
+ dtype = u;
3660
+ }
3661
+
3662
+ return dtype;
3663
+ }
3664
+
3665
+ const ndt_t *
3666
+ typeof_data(const PyObject *data, bool shortcut)
3667
+ {
3668
+ NDT_STATIC_CONTEXT(ctx);
3669
+ uint16_opt_t align = {None, 0};
3670
+ const ndt_t *dtype = NULL;
3671
+ bool opt = false;
3672
+ uint32_t dtypes;
3673
+
3674
+ dtypes = fast_dtypes(&opt, data);
3675
+
3676
+ switch (dtypes) {
3677
+ case XND_BOOL:
3678
+ dtype = ndt_primitive(Bool, opt, &ctx);
3679
+ break;
3680
+ case XND_FLOAT64:
3681
+ dtype = ndt_primitive(Float64, opt, &ctx);
3682
+ break;
3683
+ case XND_COMPLEX128:
3684
+ dtype = ndt_primitive(Complex128, opt, &ctx);
3685
+ break;
3686
+ case XND_INT64:
3687
+ dtype = ndt_primitive(Int64, opt, &ctx);
3688
+ break;
3689
+ case XND_STRING:
3690
+ dtype = ndt_string(opt, &ctx);
3691
+ break;
3692
+ case XND_BYTES:
3693
+ dtype = ndt_bytes(align, opt, &ctx);
3694
+ break;
3695
+ default:
3696
+ dtype = unify_dtypes(data, shortcut);
3697
+ if (dtype == NULL) {
3698
+ return NULL;
3699
+ }
3700
+ break;
3701
+ }
3702
+
3703
+ if (dtype == NULL) {
3704
+ return seterr_ndt(&ctx);
3705
+ }
3706
+
3707
+ return dtype;
3708
+ }
3709
+
3710
+
3711
+ /**********************************************************************/
3712
+ /* Main type inference */
3713
+ /**********************************************************************/
3714
+
3715
+ static const ndt_t *
3716
+ typeof_list_top(PyObject *v, const ndt_t *dtype)
3717
+ {
3718
+ PyObject *tuple;
3719
+ PyObject *shapes;
3720
+ const ndt_t *t;
3721
+
3722
+ assert(PyList_Check(v));
3723
+
3724
+ tuple = data_shapes(NULL, v);
3725
+ if (tuple == NULL) {
3726
+ return NULL;
3727
+ }
3728
+ shapes = PyTuple_GET_ITEM(tuple, 1);
3729
+
3730
+ if (require_var(shapes)) {
3731
+ t = var_from_shapes(shapes, dtype);
3732
+ }
3733
+ else {
3734
+ t = fixed_from_shapes(shapes, dtype);
3735
+ }
3736
+
3737
+ Py_DECREF(tuple);
3738
+ return t;
3739
+ }
3740
+
3741
+ static const ndt_t *
3742
+ typeof_list(PyObject *v, bool shortcut)
3743
+ {
3744
+ PyObject *tuple;
3745
+ PyObject *data;
3746
+ PyObject *shapes;
3747
+ const ndt_t *t, *dtype;
3748
+
3749
+ assert(PyList_Check(v));
3750
+
3751
+ tuple = data_shapes(NULL, v);
3752
+ if (tuple == NULL) {
3753
+ return NULL;
3754
+ }
3755
+ data = PyTuple_GET_ITEM(tuple, 0);
3756
+ shapes = PyTuple_GET_ITEM(tuple, 1);
3757
+
3758
+ dtype = typeof_data(data, shortcut);
3759
+ if (dtype == NULL) {
3760
+ Py_DECREF(tuple);
3761
+ return NULL;
3762
+ }
3763
+
3764
+ if (require_var(shapes)) {
3765
+ t = var_from_shapes(shapes, dtype);
3766
+ }
3767
+ else {
3768
+ t = fixed_from_shapes(shapes, dtype);
3769
+ }
3770
+
3771
+ ndt_decref(dtype);
3772
+ Py_DECREF(tuple);
3773
+ return t;
3774
+ }
3775
+
3776
+ static const ndt_t *
3777
+ typeof_tuple(PyObject *v, bool replace_any, bool shortcut)
3778
+ {
3779
+ NDT_STATIC_CONTEXT(ctx);
3780
+ uint16_opt_t none = {None, 0};
3781
+ ndt_field_t *fields;
3782
+ const ndt_t *t;
3783
+ int64_t shape;
3784
+ int64_t i;
3785
+
3786
+ assert(PyTuple_Check(v));
3787
+
3788
+ shape = PyTuple_GET_SIZE(v);
3789
+ if (shape == 0) {
3790
+ t = ndt_tuple(Nonvariadic, NULL, 0, none, none, false, &ctx);
3791
+ return t == NULL ? seterr_ndt(&ctx) : t;
3792
+ }
3793
+
3794
+ fields = ndt_calloc(shape, sizeof *fields);
3795
+ if (fields == NULL) {
3796
+ PyErr_NoMemory();
3797
+ return NULL;
3798
+ }
3799
+
3800
+ for (i = 0; i < shape; i++) {
3801
+ t = typeof(PyTuple_GET_ITEM(v, i), replace_any, shortcut);
3802
+ if (t == NULL) {
3803
+ ndt_field_array_del(fields, i);
3804
+ return NULL;
3805
+ }
3806
+
3807
+ fields[i].access = t->access;
3808
+ fields[i].name = NULL;
3809
+ fields[i].type = t;
3810
+ if (fields[i].access == Concrete) {
3811
+ fields[i].Concrete.align = t->align;
3812
+ fields[i].Concrete.explicit_align = false;
3813
+ fields[i].Concrete.pad = UINT16_MAX;
3814
+ fields[i].Concrete.explicit_pad = false;
3815
+ }
3816
+ }
3817
+
3818
+ t = ndt_tuple(Nonvariadic, fields, shape, none, none, false, &ctx);
3819
+ ndt_field_array_del(fields, shape);
3820
+ return t == NULL ? seterr_ndt(&ctx) : t;
3821
+ }
3822
+
3823
+ static const ndt_t *
3824
+ typeof_dict(PyObject *v, bool replace_any, bool shortcut)
3825
+ {
3826
+ NDT_STATIC_CONTEXT(ctx);
3827
+ uint16_opt_t none = {None, 0};
3828
+ PyObject *keys = NULL;
3829
+ PyObject *values = NULL;
3830
+ ndt_field_t *fields;
3831
+ const ndt_t *t;
3832
+ const char *cp;
3833
+ char *name;
3834
+ int64_t shape;
3835
+ int64_t i;
3836
+
3837
+ assert(PyDict_Check(v));
3838
+
3839
+ shape = PyMapping_Size(v);
3840
+ if (shape == 0) {
3841
+ t = ndt_record(Nonvariadic, NULL, 0, none, none, false, &ctx);
3842
+ return t == NULL ? seterr_ndt(&ctx) : t;
3843
+ }
3844
+
3845
+ keys = PyMapping_Keys(v);
3846
+ if (keys == NULL) {
3847
+ return NULL;
3848
+ }
3849
+
3850
+ values = PyMapping_Values(v);
3851
+ if (values == NULL) {
3852
+ Py_DECREF(keys);
3853
+ return NULL;
3854
+ }
3855
+
3856
+ fields = ndt_calloc(shape, sizeof *fields);
3857
+ if (fields == NULL) {
3858
+ Py_DECREF(keys);
3859
+ Py_DECREF(values);
3860
+ PyErr_NoMemory();
3861
+ return NULL;
3862
+ }
3863
+
3864
+ for (i = 0; i < shape; i++) {
3865
+ t = typeof(PyList_GET_ITEM(values, i), replace_any, shortcut);
3866
+ if (t == NULL) {
3867
+ ndt_field_array_del(fields, i);
3868
+ Py_DECREF(keys);
3869
+ Py_DECREF(values);
3870
+ return NULL;
3871
+ }
3872
+
3873
+ cp = PyUnicode_AsUTF8(PyList_GET_ITEM(keys, i));
3874
+ if (cp == NULL) {
3875
+ ndt_field_array_del(fields, i);
3876
+ ndt_decref(t);
3877
+ Py_DECREF(keys);
3878
+ Py_DECREF(values);
3879
+ return NULL;
3880
+ }
3881
+
3882
+ name = ndt_strdup(cp, &ctx);
3883
+ if (name == NULL) {
3884
+ ndt_field_array_del(fields, i);
3885
+ ndt_decref(t);
3886
+ Py_DECREF(keys);
3887
+ Py_DECREF(values);
3888
+ return seterr_ndt(&ctx);
3889
+ }
3890
+
3891
+ fields[i].access = t->access;
3892
+ fields[i].name = name;
3893
+ fields[i].type = t;
3894
+ if (fields[i].access == Concrete) {
3895
+ fields[i].Concrete.align = t->align;
3896
+ fields[i].Concrete.explicit_align = false;
3897
+ fields[i].Concrete.pad = UINT16_MAX;
3898
+ fields[i].Concrete.explicit_pad = false;
3899
+ }
3900
+ }
3901
+
3902
+ Py_DECREF(keys);
3903
+ Py_DECREF(values);
3904
+
3905
+ t = ndt_record(Nonvariadic, fields, shape, none, none, false, &ctx);
3906
+ ndt_field_array_del(fields, shape);
3907
+ return t == NULL ? seterr_ndt(&ctx) : t;
3908
+ }
3909
+
3910
+ static const ndt_t *
3911
+ typeof(PyObject *v, bool replace_any, bool shortcut)
3912
+ {
3913
+ NDT_STATIC_CONTEXT(ctx);
3914
+ const ndt_t *t;
3915
+
3916
+ if (PyList_Check(v)) {
3917
+ return typeof_list(v, shortcut);
3918
+ }
3919
+ if (PyTuple_Check(v)) {
3920
+ return typeof_tuple(v, replace_any, shortcut);
3921
+ }
3922
+ if (PyDict_Check(v)) {
3923
+ return typeof_dict(v, replace_any, shortcut);
3924
+ }
3925
+
3926
+ if (PyBool_Check(v)) {
3927
+ t = ndt_primitive(Bool, 0, &ctx);
3928
+ }
3929
+ else if (PyFloat_Check(v)) {
3930
+ t = ndt_primitive(Float64, 0, &ctx);
3931
+ }
3932
+ else if (PyComplex_Check(v)) {
3933
+ t = ndt_primitive(Complex128, 0, &ctx);
3934
+ }
3935
+ else if (PyLong_Check(v)) {
3936
+ t = ndt_primitive(Int64, 0, &ctx);
3937
+ }
3938
+ else if (PyUnicode_Check(v)) {
3939
+ t = ndt_string(false, &ctx);
3940
+ }
3941
+ else if (PyBytes_Check(v)) {
3942
+ uint16_opt_t align = {None, 0};
3943
+ t = ndt_bytes(align, false, &ctx);
3944
+ }
3945
+ else if (v == Py_None) {
3946
+ if (replace_any) {
3947
+ t = ndt_primitive(Float64, NDT_OPTION, &ctx);
3948
+ }
3949
+ else {
3950
+ t = ndt_any_kind(true, &ctx);
3951
+ }
3952
+ }
3953
+ else {
3954
+ PyErr_SetString(PyExc_ValueError, "type inference failed");
3955
+ return NULL;
3956
+ }
3957
+
3958
+ return t == NULL ? seterr_ndt(&ctx) : t;
3959
+ }
3960
+
3961
+ static PyObject *
3962
+ xnd_typeof(PyObject *m UNUSED, PyObject *args, PyObject *kwds)
3963
+ {
3964
+ static char *kwlist[] = {"value", "dtype", "shortcut", NULL};
3965
+ PyObject *value = NULL;
3966
+ PyObject *dtype = Py_None;
3967
+ PyObject *ret;
3968
+ const ndt_t *t;
3969
+ int shortcut = 0;
3970
+
3971
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|Op", kwlist, &value,
3972
+ &dtype, &shortcut)) {
3973
+ return NULL;
3974
+ }
3975
+
3976
+ if (dtype != Py_None) {
3977
+ if (!Ndt_Check(dtype)) {
3978
+ PyErr_Format(PyExc_ValueError, "dtype argument must be ndt");
3979
+ return NULL;
3980
+ }
3981
+
3982
+ if (PyList_Check(value)) {
3983
+ t = typeof_list_top(value, NDT(dtype));
3984
+ }
3985
+ else {
3986
+ t = NDT(dtype);
3987
+ ndt_incref(t);
3988
+ }
3989
+ }
3990
+ else {
3991
+ t = typeof(value, true, (bool)shortcut);
3992
+ }
3993
+
3994
+ if (t == NULL) {
3995
+ return NULL;
3996
+ }
3997
+
3998
+ ret = Ndt_FromType(t);
3999
+
4000
+ ndt_decref(t);
4001
+ return ret;
4002
+ }
4003
+
4004
+
4005
+ /****************************************************************************/
4006
+ /* C-API */
4007
+ /****************************************************************************/
4008
+
4009
+ static void **xnd_api[XND_MAX_API];
4010
+
4011
+ static int
4012
+ Xnd_CheckExact(const PyObject *v)
4013
+ {
4014
+ return Py_TYPE(v) == &Xnd_Type;
4015
+ }
4016
+
4017
+ static int
4018
+ Xnd_Check(const PyObject *v)
4019
+ {
4020
+ return PyObject_TypeCheck(v, &Xnd_Type);
4021
+ }
4022
+
4023
+ static const xnd_t *
4024
+ CONST_XND(const PyObject *v)
4025
+ {
4026
+ assert(Xnd_Check(v));
4027
+ return &((XndObject *)v)->xnd;
4028
+ }
4029
+
4030
+ static PyObject *
4031
+ Xnd_EmptyFromType(PyTypeObject *tp, const ndt_t *t, uint32_t flags)
2484
4032
  {
2485
4033
  MemoryBlockObject *mblock;
2486
4034
  PyObject *type;
@@ -2490,7 +4038,7 @@ Xnd_EmptyFromType(PyTypeObject *tp, ndt_t *t)
2490
4038
  return NULL;
2491
4039
  }
2492
4040
 
2493
- mblock = mblock_empty(type);
4041
+ mblock = mblock_empty(type, flags);
2494
4042
  Py_DECREF(type);
2495
4043
  if (mblock == NULL) {
2496
4044
  return NULL;
@@ -2508,14 +4056,15 @@ Xnd_ViewMoveNdt(const PyObject *v, ndt_t *t)
2508
4056
 
2509
4057
  if (!Xnd_Check(v)) {
2510
4058
  PyErr_SetString(PyExc_TypeError, "expected xnd object");
2511
- ndt_del(t);
4059
+ ndt_decref(t);
2512
4060
  return NULL;
2513
4061
  }
2514
4062
 
2515
- type = Ndt_MoveSubtree(src->type, t);
4063
+ type = Ndt_FromType(t);
2516
4064
  if (type == NULL) {
2517
4065
  return NULL;
2518
4066
  }
4067
+ ndt_decref(t);
2519
4068
 
2520
4069
  view = pyxnd_alloc(Py_TYPE(src));
2521
4070
  if (view == NULL) {
@@ -2571,7 +4120,7 @@ Xnd_FromXndMoveType(const PyObject *xnd, xnd_t *x)
2571
4120
  if (!Xnd_Check(xnd)) {
2572
4121
  PyErr_SetString(PyExc_TypeError,
2573
4122
  "Xnd_FromXndMoveType() called on non-xnd object");
2574
- ndt_del((ndt_t *)x->type);
4123
+ ndt_decref(x->type);
2575
4124
  return NULL;
2576
4125
  }
2577
4126
 
@@ -2700,7 +4249,7 @@ _test_view_new(PyObject *module UNUSED, PyObject *args UNUSED)
2700
4249
  NDT_STATIC_CONTEXT(ctx);
2701
4250
  xnd_view_t x = xnd_view_error;
2702
4251
  double *d;
2703
- ndt_t *t;
4252
+ const ndt_t *t;
2704
4253
  char *ptr;
2705
4254
 
2706
4255
  t = ndt_from_string("3 * float64", &ctx);
@@ -2710,7 +4259,7 @@ _test_view_new(PyObject *module UNUSED, PyObject *args UNUSED)
2710
4259
 
2711
4260
  ptr = ndt_aligned_calloc(8, 3 * sizeof(double));
2712
4261
  if (ptr == NULL) {
2713
- ndt_del(t);
4262
+ ndt_decref(t);
2714
4263
  (void)ndt_memory_error(&ctx);
2715
4264
  return seterr(&ctx);
2716
4265
  }
@@ -2736,6 +4285,8 @@ _test_view_new(PyObject *module UNUSED, PyObject *args UNUSED)
2736
4285
 
2737
4286
  static PyMethodDef _xnd_methods [] =
2738
4287
  {
4288
+ { "data_shapes", (PyCFunction)data_shapes, METH_O, NULL},
4289
+ { "_typeof", (PyCFunction)xnd_typeof, METH_VARARGS|METH_KEYWORDS, NULL},
2739
4290
  { "_test_view_subscript", (PyCFunction)_test_view_subscript, METH_VARARGS|METH_KEYWORDS, NULL},
2740
4291
  { "_test_view_new", (PyCFunction)_test_view_new, METH_NOARGS, NULL},
2741
4292
  { NULL, NULL, 1, NULL }