xnd 0.2.0dev6 → 0.2.0dev7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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 }