xnd 0.2.0dev3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (93) hide show
  1. checksums.yaml +7 -0
  2. data/CONTRIBUTING.md +42 -0
  3. data/Gemfile +3 -0
  4. data/History.md +0 -0
  5. data/README.md +7 -0
  6. data/Rakefile +135 -0
  7. data/ext/ruby_xnd/extconf.rb +70 -0
  8. data/ext/ruby_xnd/float_pack_unpack.c +277 -0
  9. data/ext/ruby_xnd/float_pack_unpack.h +39 -0
  10. data/ext/ruby_xnd/gc_guard.c +36 -0
  11. data/ext/ruby_xnd/gc_guard.h +12 -0
  12. data/ext/ruby_xnd/include/xnd.h +449 -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/memory_block_object.c +32 -0
  18. data/ext/ruby_xnd/memory_block_object.h +33 -0
  19. data/ext/ruby_xnd/ruby_xnd.c +1953 -0
  20. data/ext/ruby_xnd/ruby_xnd.h +61 -0
  21. data/ext/ruby_xnd/ruby_xnd_internal.h +85 -0
  22. data/ext/ruby_xnd/util.h +170 -0
  23. data/ext/ruby_xnd/xnd/AUTHORS.txt +5 -0
  24. data/ext/ruby_xnd/xnd/INSTALL.txt +134 -0
  25. data/ext/ruby_xnd/xnd/LICENSE.txt +29 -0
  26. data/ext/ruby_xnd/xnd/MANIFEST.in +3 -0
  27. data/ext/ruby_xnd/xnd/Makefile.in +80 -0
  28. data/ext/ruby_xnd/xnd/README.rst +44 -0
  29. data/ext/ruby_xnd/xnd/config.guess +1530 -0
  30. data/ext/ruby_xnd/xnd/config.h.in +22 -0
  31. data/ext/ruby_xnd/xnd/config.sub +1782 -0
  32. data/ext/ruby_xnd/xnd/configure +4867 -0
  33. data/ext/ruby_xnd/xnd/configure.ac +164 -0
  34. data/ext/ruby_xnd/xnd/doc/Makefile +14 -0
  35. data/ext/ruby_xnd/xnd/doc/_static/copybutton.js +66 -0
  36. data/ext/ruby_xnd/xnd/doc/conf.py +26 -0
  37. data/ext/ruby_xnd/xnd/doc/index.rst +44 -0
  38. data/ext/ruby_xnd/xnd/doc/libxnd/data-structures.rst +186 -0
  39. data/ext/ruby_xnd/xnd/doc/libxnd/functions.rst +148 -0
  40. data/ext/ruby_xnd/xnd/doc/libxnd/index.rst +25 -0
  41. data/ext/ruby_xnd/xnd/doc/releases/index.rst +34 -0
  42. data/ext/ruby_xnd/xnd/doc/xnd/align-pack.rst +96 -0
  43. data/ext/ruby_xnd/xnd/doc/xnd/buffer-protocol.rst +42 -0
  44. data/ext/ruby_xnd/xnd/doc/xnd/index.rst +30 -0
  45. data/ext/ruby_xnd/xnd/doc/xnd/quickstart.rst +62 -0
  46. data/ext/ruby_xnd/xnd/doc/xnd/types.rst +674 -0
  47. data/ext/ruby_xnd/xnd/install-sh +527 -0
  48. data/ext/ruby_xnd/xnd/libxnd/Makefile.in +102 -0
  49. data/ext/ruby_xnd/xnd/libxnd/Makefile.vc +112 -0
  50. data/ext/ruby_xnd/xnd/libxnd/bitmaps.c +345 -0
  51. data/ext/ruby_xnd/xnd/libxnd/contrib.h +313 -0
  52. data/ext/ruby_xnd/xnd/libxnd/copy.c +944 -0
  53. data/ext/ruby_xnd/xnd/libxnd/equal.c +1216 -0
  54. data/ext/ruby_xnd/xnd/libxnd/inline.h +154 -0
  55. data/ext/ruby_xnd/xnd/libxnd/overflow.h +147 -0
  56. data/ext/ruby_xnd/xnd/libxnd/split.c +286 -0
  57. data/ext/ruby_xnd/xnd/libxnd/tests/Makefile.in +39 -0
  58. data/ext/ruby_xnd/xnd/libxnd/tests/Makefile.vc +44 -0
  59. data/ext/ruby_xnd/xnd/libxnd/tests/README.txt +2 -0
  60. data/ext/ruby_xnd/xnd/libxnd/tests/runtest.c +101 -0
  61. data/ext/ruby_xnd/xnd/libxnd/tests/test.h +48 -0
  62. data/ext/ruby_xnd/xnd/libxnd/tests/test_fixed.c +108 -0
  63. data/ext/ruby_xnd/xnd/libxnd/xnd.c +1304 -0
  64. data/ext/ruby_xnd/xnd/libxnd/xnd.h +449 -0
  65. data/ext/ruby_xnd/xnd/python/test_xnd.py +3144 -0
  66. data/ext/ruby_xnd/xnd/python/xnd/__init__.py +290 -0
  67. data/ext/ruby_xnd/xnd/python/xnd/_xnd.c +2822 -0
  68. data/ext/ruby_xnd/xnd/python/xnd/contrib/pretty.py +850 -0
  69. data/ext/ruby_xnd/xnd/python/xnd/docstrings.h +129 -0
  70. data/ext/ruby_xnd/xnd/python/xnd/pyxnd.h +200 -0
  71. data/ext/ruby_xnd/xnd/python/xnd/util.h +182 -0
  72. data/ext/ruby_xnd/xnd/python/xnd_randvalue.py +1121 -0
  73. data/ext/ruby_xnd/xnd/python/xnd_support.py +106 -0
  74. data/ext/ruby_xnd/xnd/setup.py +303 -0
  75. data/ext/ruby_xnd/xnd/vcbuild/INSTALL.txt +42 -0
  76. data/ext/ruby_xnd/xnd/vcbuild/runtest32.bat +16 -0
  77. data/ext/ruby_xnd/xnd/vcbuild/runtest64.bat +14 -0
  78. data/ext/ruby_xnd/xnd/vcbuild/vcbuild32.bat +29 -0
  79. data/ext/ruby_xnd/xnd/vcbuild/vcbuild64.bat +29 -0
  80. data/ext/ruby_xnd/xnd/vcbuild/vcclean.bat +13 -0
  81. data/ext/ruby_xnd/xnd/vcbuild/vcdistclean.bat +14 -0
  82. data/lib/ruby_xnd.so +0 -0
  83. data/lib/xnd.rb +306 -0
  84. data/lib/xnd/monkeys.rb +29 -0
  85. data/lib/xnd/version.rb +6 -0
  86. data/spec/debug_spec.rb +9 -0
  87. data/spec/gc_guard_spec.rb +10 -0
  88. data/spec/leakcheck.rb +9 -0
  89. data/spec/spec_helper.rb +877 -0
  90. data/spec/type_inference_spec.rb +81 -0
  91. data/spec/xnd_spec.rb +2921 -0
  92. data/xnd.gemspec +47 -0
  93. metadata +215 -0
@@ -0,0 +1,290 @@
1
+ #
2
+ # BSD 3-Clause License
3
+ #
4
+ # Copyright (c) 2017-2018, plures
5
+ # All rights reserved.
6
+ #
7
+ # Redistribution and use in source and binary forms, with or without
8
+ # modification, are permitted provided that the following conditions are met:
9
+ #
10
+ # 1. Redistributions of source code must retain the above copyright notice,
11
+ # this list of conditions and the following disclaimer.
12
+ #
13
+ # 2. Redistributions in binary form must reproduce the above copyright notice,
14
+ # this list of conditions and the following disclaimer in the documentation
15
+ # and/or other materials provided with the distribution.
16
+ #
17
+ # 3. Neither the name of the copyright holder nor the names of its
18
+ # contributors may be used to endorse or promote products derived from
19
+ # this software without specific prior written permission.
20
+ #
21
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
22
+ # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23
+ # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
24
+ # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
25
+ # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26
+ # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
27
+ # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
28
+ # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
29
+ # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30
+ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31
+ #
32
+
33
+ """
34
+ Xnd implements a container for mapping all Python values relevant for
35
+ scientific computing directly to memory.
36
+
37
+ xnd supports ragged arrays, categorical types, indexing, slicing, aligned
38
+ memory blocks and type inference.
39
+
40
+ Operations like indexing and slicing return zero-copy typed views on the
41
+ data.
42
+
43
+ Importing PEP-3118 buffers is supported.
44
+ """
45
+
46
+
47
+ # Ensure that libndtypes is loaded and initialized.
48
+ from ndtypes import ndt, instantiate, MAX_DIM
49
+ from ._xnd import Xnd, XndEllipsis
50
+ from itertools import accumulate
51
+ from .contrib.pretty import pretty
52
+
53
+ __all__ = ['xnd', 'XndEllipsis', 'typeof', '_typeof']
54
+
55
+
56
+ # ======================================================================
57
+ # xnd object
58
+ # ======================================================================
59
+
60
+ class xnd(Xnd):
61
+ """General container type for unboxing a wide range of Python values
62
+ to typed memory blocks.
63
+
64
+ Operations like indexing and slicing return zero-copy typed views
65
+ on the data.
66
+
67
+ Create fixed or ragged arrays:
68
+
69
+ >>> xnd([[1,2,3], [4,5,6]])
70
+ xnd([[1, 2, 3], [4, 5, 6]], type="2 * 3 * int64")
71
+
72
+ >>> xnd([[1,2,3], [4]])
73
+ xnd([[1, 2, 3], [4]], type="var * var * int64")
74
+
75
+ Create a record:
76
+
77
+ >>> xnd({'a': "xyz", 'b': [1, 2, 3]})
78
+ xnd({'a': 'xyz', 'b': [1, 2, 3]}, type="{a : string, b : 3 * int64}")
79
+
80
+ Create a categorical type:
81
+
82
+ >>> xnd(['a', 'b', None, 'a'], levels=['a', 'b', None])
83
+ xnd(['a', 'b', None, 'a'], type="4 * categorical('a', 'b', NA)")
84
+
85
+ Create an explicitly typed memory block:
86
+
87
+ >>> xnd(100000 * [1], type="100000 * uint8")
88
+ xnd([1, 1, 1, 1, 1, 1, 1, 1, 1, ...], type="100000 * uint8")
89
+
90
+ Create an empty (zero initialized) memory block:
91
+
92
+ >>> xnd.empty("100000 * uint8")
93
+ xnd([0, 0, 0, 0, 0, 0, 0, 0, 0, ...], type="100000 * uint8")
94
+
95
+ Import a memory block from a buffer exporter:
96
+
97
+ >>> xnd.from_buffer(b"123")
98
+ xnd([49, 50, 51], type="3 * uint8")
99
+ """
100
+
101
+ def __new__(cls, value, *, type=None, dtype=None, levels=None,
102
+ typedef=None, dtypedef=None):
103
+ if (type, dtype, levels, typedef, dtypedef).count(None) < 2:
104
+ raise TypeError(
105
+ "the 'type', 'dtype', 'levels' and 'typedef' arguments are "
106
+ "mutually exclusive")
107
+ if type is not None:
108
+ if isinstance(type, str):
109
+ type = ndt(type)
110
+ elif dtype is not None:
111
+ type = typeof(value, dtype=dtype)
112
+ elif levels is not None:
113
+ args = ', '.join("'%s'" % l if l is not None else 'NA' for l in levels)
114
+ t = "%d * categorical(%s)" % (len(value), args)
115
+ type = ndt(t)
116
+ elif typedef is not None:
117
+ type = ndt(typedef)
118
+ if type.isabstract():
119
+ dtype = type.hidden_dtype
120
+ t = typeof(value, dtype=dtype)
121
+ type = instantiate(typedef, t)
122
+ elif dtypedef is not None:
123
+ dtype = ndt(dtypedef)
124
+ type = typeof(value, dtype=dtype)
125
+ else:
126
+ type = typeof(value)
127
+ return super().__new__(cls, type=type, value=value)
128
+
129
+ def __repr__(self):
130
+ value = self.short_value(maxshape=10)
131
+ fmt = pretty((value, "@type='%s'@" % self.type), max_width=120)
132
+ fmt = fmt.replace('"@', "")
133
+ fmt = fmt.replace('@"', "")
134
+ fmt = fmt.replace("\n", "\n ")
135
+ return "xnd%s" % fmt
136
+
137
+ @classmethod
138
+ def unsafe_from_data(cls, obj=None, type=None):
139
+ """Return an xnd object that obtains memory from 'obj' via the
140
+ buffer protocol. The buffer protocol's type is overridden by
141
+ 'type'. No safety checks are performed, the user is responsible
142
+ for passing a suitable type.
143
+ """
144
+ if isinstance(type, str):
145
+ type = ndt(type)
146
+ return cls._unsafe_from_data(obj, type)
147
+
148
+
149
+ # ======================================================================
150
+ # Type inference
151
+ # ======================================================================
152
+
153
+ def typeof(value, *, dtype=None):
154
+ return ndt(_typeof(value, dtype=dtype))
155
+
156
+ def _choose_dtype(lst):
157
+ for x in lst:
158
+ if x is not None:
159
+ return _typeof(x)
160
+ return "float64"
161
+
162
+ def _typeof(value, *, dtype=None):
163
+ """Infer the type of a Python value. Only a subset of Datashape is
164
+ supported. In general, types need to be explicitly specified when
165
+ creating xnd objects.
166
+ """
167
+ if isinstance(value, list):
168
+ data, shapes = data_shapes(value)
169
+ opt = None in data
170
+
171
+ if dtype is None:
172
+ if not data:
173
+ dtype = 'float64'
174
+ else:
175
+ dtype = _choose_dtype(data)
176
+ for x in data:
177
+ if x is not None:
178
+ t = _typeof(x)
179
+ if t != dtype:
180
+ raise ValueError("dtype mismatch: have %s and %s" % (dtype, t))
181
+
182
+ if opt:
183
+ dtype = '?' + dtype
184
+
185
+ t = dtype
186
+ var = any(len(set(lst)) > 1 or None in lst for lst in shapes)
187
+ for lst in shapes:
188
+ opt = None in lst
189
+ lst = [0 if x is None else x for x in lst]
190
+ t = add_dim(opt=opt, shapes=lst, typ=t, use_var=var)
191
+
192
+ return t
193
+
194
+ elif dtype is not None:
195
+ raise TypeError("dtype argument is only supported for arrays")
196
+
197
+ elif isinstance(value, tuple):
198
+ return "(" + ", ".join([_typeof(x) for x in value]) + ")"
199
+
200
+ elif isinstance(value, dict):
201
+ if all(isinstance(k, str) for k in value):
202
+ return "{" + ", ".join(["%s: %s" % (k, _typeof(v)) for k, v in value.items()]) + "}"
203
+ raise ValueError("all dict keys must be strings")
204
+
205
+ elif value is None:
206
+ return '?float64'
207
+
208
+ elif isinstance(value, float):
209
+ return 'float64'
210
+
211
+ elif isinstance(value, complex):
212
+ return 'complex128'
213
+
214
+ elif isinstance(value, int):
215
+ return 'int64'
216
+
217
+ elif isinstance(value, str):
218
+ return 'string'
219
+
220
+ elif isinstance(value, bytes):
221
+ return 'bytes'
222
+
223
+ else:
224
+ raise ValueError("cannot infer type for %r" % value)
225
+
226
+
227
+ def add_dim(*, opt=False, shapes=None, typ=None, use_var=False):
228
+ """Construct a new dimension type based on the list of 'shapes' that
229
+ are present in a dimension.
230
+ """
231
+ if use_var:
232
+ offsets = [0] + list(accumulate(shapes))
233
+ return "%svar(offsets=%s) * %s" % ('?' if opt else '', offsets, typ)
234
+ else:
235
+ n = len(set(shapes))
236
+ assert n <= 1 and not None in shapes
237
+ shape = 0 if n == 0 else shapes[0]
238
+ return "%d * %s" % (shape, typ)
239
+
240
+ def data_shapes(tree):
241
+ """Extract array data and dimension shapes from a nested list. The
242
+ list may contain None for missing data or dimensions.
243
+
244
+ >>> data_shapes([[0, 1], [2, 3, 4], [5, 6, 7, 8]])
245
+ ([0, 1, 2, 3, 4, 5, 6, 7, 8], [[2, 3, 4], [3]])
246
+ ^ ^ ^
247
+ | | `--- ndim=2: single shape 3
248
+ | `-- ndim=1: shapes 2, 3, 4
249
+ `--- ndim=0: extracted array data
250
+ """
251
+ acc = [[] for _ in range(MAX_DIM+1)]
252
+ min_level = MAX_DIM + 1
253
+ max_level = 0
254
+
255
+ def search(level, a):
256
+ nonlocal min_level, max_level
257
+
258
+ if level > MAX_DIM:
259
+ raise ValueError("too many dimensions")
260
+
261
+ current = acc[level]
262
+ if a is None:
263
+ current.append(a)
264
+ elif isinstance(a, list):
265
+ current.append(len(a))
266
+ next_level = level + 1
267
+ max_level = max(next_level, max_level)
268
+ if not a:
269
+ min_level = min(next_level, min_level)
270
+ else:
271
+ for item in a:
272
+ search(level+1, item)
273
+ else:
274
+ acc[max_level].append(a)
275
+ min_level = min(level, min_level)
276
+
277
+ search(max_level, tree)
278
+ if acc[max_level] and all(x is None for x in acc[max_level]):
279
+ pass # min_level is not set in this special case, hence the check.
280
+ elif min_level != max_level:
281
+ raise ValueError("unbalanced tree: min depth: %d max depth: %d" %
282
+ (min_level, max_level))
283
+
284
+ data = acc[max_level]
285
+ shapes = list(reversed(acc[0:max_level]))
286
+
287
+ return data, shapes
288
+
289
+
290
+
@@ -0,0 +1,2822 @@
1
+ /*
2
+ * BSD 3-Clause License
3
+ *
4
+ * Copyright (c) 2017-2018, plures
5
+ * All rights reserved.
6
+ *
7
+ * Redistribution and use in source and binary forms, with or without
8
+ * modification, are permitted provided that the following conditions are met:
9
+ *
10
+ * 1. Redistributions of source code must retain the above copyright notice,
11
+ * this list of conditions and the following disclaimer.
12
+ *
13
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
14
+ * this list of conditions and the following disclaimer in the documentation
15
+ * and/or other materials provided with the distribution.
16
+ *
17
+ * 3. Neither the name of the copyright holder nor the names of its
18
+ * contributors may be used to endorse or promote products derived from
19
+ * this software without specific prior written permission.
20
+ *
21
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
22
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
24
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
25
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
27
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
28
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
29
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31
+ */
32
+
33
+
34
+ #include <Python.h>
35
+ #include "complexobject.h"
36
+ #include <stdlib.h>
37
+ #include <stdint.h>
38
+ #include <inttypes.h>
39
+ #include <stdbool.h>
40
+ #include "ndtypes.h"
41
+ #include "pyndtypes.h"
42
+ #include "xnd.h"
43
+ #include "util.h"
44
+ #include "docstrings.h"
45
+
46
+ #define XND_MODULE
47
+ #include "pyxnd.h"
48
+
49
+
50
+ #ifdef _MSC_VER
51
+ #ifndef UNUSED
52
+ #define UNUSED
53
+ #endif
54
+ #else
55
+ #if defined(__GNUC__) && !defined(__INTEL_COMPILER)
56
+ #define UNUSED __attribute__((unused))
57
+ #else
58
+ #define UNUSED
59
+ #endif
60
+ #endif
61
+
62
+
63
+ /****************************************************************************/
64
+ /* Error handling */
65
+ /****************************************************************************/
66
+
67
+ static PyObject *
68
+ seterr(ndt_context_t *ctx)
69
+ {
70
+ return Ndt_SetError(ctx);
71
+ }
72
+
73
+ static int
74
+ seterr_int(ndt_context_t *ctx)
75
+ {
76
+ (void)Ndt_SetError(ctx);
77
+ return -1;
78
+ }
79
+
80
+
81
+ /****************************************************************************/
82
+ /* Singletons */
83
+ /****************************************************************************/
84
+
85
+ static PyTypeObject XndEllipsis_Type;
86
+
87
+ static PyObject XndEllipsisObject = {
88
+ _PyObject_EXTRA_INIT
89
+ .ob_refcnt = 1,
90
+ .ob_type = &XndEllipsis_Type
91
+ };
92
+
93
+ static PyObject *
94
+ xnd_ellipsis(void)
95
+ {
96
+ Py_INCREF(&XndEllipsisObject);
97
+ return &XndEllipsisObject;
98
+ }
99
+
100
+ static PyObject *
101
+ xnd_ellipsis_repr(PyObject *self UNUSED)
102
+ {
103
+ return PyUnicode_FromString("...");
104
+ }
105
+
106
+ static PyTypeObject XndEllipsis_Type = {
107
+ PyVarObject_HEAD_INIT(NULL, 0)
108
+ .tp_name = "_xnd.XndEllipsis",
109
+ .tp_repr = xnd_ellipsis_repr
110
+ };
111
+
112
+
113
+ /****************************************************************************/
114
+ /* MemoryBlock Object */
115
+ /****************************************************************************/
116
+
117
+ static int mblock_init(xnd_t * const x, PyObject *v);
118
+ static PyTypeObject MemoryBlock_Type;
119
+
120
+
121
+ static MemoryBlockObject *
122
+ mblock_alloc(void)
123
+ {
124
+ MemoryBlockObject *self;
125
+
126
+ self = PyObject_GC_New(MemoryBlockObject, &MemoryBlock_Type);
127
+ if (self == NULL) {
128
+ return NULL;
129
+ }
130
+
131
+ self->type = NULL;
132
+ self->xnd = NULL;
133
+ self->view = NULL;
134
+
135
+ PyObject_GC_Track(self);
136
+ return self;
137
+ }
138
+
139
+ static int
140
+ mblock_traverse(MemoryBlockObject *self, visitproc visit, void *arg)
141
+ {
142
+ Py_VISIT(self->type);
143
+ if (self->view) {
144
+ Py_VISIT(self->view->obj);
145
+ }
146
+ return 0;
147
+ }
148
+
149
+ static void
150
+ mblock_dealloc(MemoryBlockObject *self)
151
+ {
152
+ PyObject_GC_UnTrack(self);
153
+ xnd_del(self->xnd);
154
+ self->xnd = NULL;
155
+ Py_CLEAR(self->type);
156
+ if (self->view) {
157
+ PyBuffer_Release(self->view);
158
+ ndt_free(self->view);
159
+ self->view = NULL;
160
+ }
161
+ PyObject_GC_Del(self);
162
+ }
163
+
164
+ static MemoryBlockObject *
165
+ mblock_empty(PyObject *type)
166
+ {
167
+ NDT_STATIC_CONTEXT(ctx);
168
+ MemoryBlockObject *self;
169
+
170
+ if (!Ndt_Check(type)) {
171
+ PyErr_SetString(PyExc_TypeError, "expected ndt object");
172
+ return NULL;
173
+ }
174
+
175
+ self = mblock_alloc();
176
+ if (self == NULL) {
177
+ return NULL;
178
+ }
179
+
180
+ self->xnd = xnd_empty_from_type(CONST_NDT(type), XND_OWN_EMBEDDED, &ctx);
181
+ if (self->xnd == NULL) {
182
+ Py_DECREF(self);
183
+ return (MemoryBlockObject *)seterr(&ctx);
184
+ }
185
+ Py_INCREF(type);
186
+ self->type = type;
187
+
188
+ return self;
189
+ }
190
+
191
+ static MemoryBlockObject *
192
+ mblock_from_typed_value(PyObject *type, PyObject *value)
193
+ {
194
+ MemoryBlockObject *self;
195
+
196
+ self = mblock_empty(type);
197
+ if (self == NULL) {
198
+ return NULL;
199
+ }
200
+
201
+ if (mblock_init(&self->xnd->master, value) < 0) {
202
+ Py_DECREF(self);
203
+ return NULL;
204
+ }
205
+
206
+ return self;
207
+ }
208
+
209
+ static MemoryBlockObject *
210
+ mblock_from_xnd(xnd_t *src)
211
+ {
212
+ NDT_STATIC_CONTEXT(ctx);
213
+ MemoryBlockObject *self;
214
+ PyObject *type;
215
+ xnd_master_t *x;
216
+
217
+ x = xnd_from_xnd(src, XND_OWN_EMBEDDED, &ctx);
218
+ if (x == NULL) {
219
+ return (MemoryBlockObject *)seterr(&ctx);
220
+ }
221
+
222
+ type = Ndt_FromType((ndt_t *)x->master.type);
223
+ if (type == NULL) {
224
+ xnd_del(x);
225
+ return NULL;
226
+ }
227
+
228
+ self = mblock_alloc();
229
+ if (self == NULL) {
230
+ Py_DECREF(type);
231
+ xnd_del(x);
232
+ return NULL;
233
+ }
234
+
235
+ self->type = type;
236
+ self->xnd = x;
237
+
238
+ return self;
239
+ }
240
+
241
+ static PyObject *
242
+ type_from_buffer(const Py_buffer *view)
243
+ {
244
+ NDT_STATIC_CONTEXT(ctx);
245
+ ndt_t *t, *type;
246
+ int64_t shape, step;
247
+ int64_t i;
248
+
249
+ if (view->buf == NULL || view->obj == NULL ||
250
+ view->format == NULL || view->suboffsets != NULL) {
251
+ PyErr_SetString(PyExc_BufferError,
252
+ "expect a buffer with full information and no suboffsets");
253
+ return NULL;
254
+ }
255
+
256
+ if (view->ndim != 0) {
257
+ if (view->shape == NULL || view->strides == NULL) {
258
+ PyErr_SetString(PyExc_BufferError,
259
+ "expect a buffer with full information");
260
+ return NULL;
261
+ }
262
+ }
263
+
264
+ type = ndt_from_bpformat(view->format, &ctx);
265
+ if (type == NULL) {
266
+ return seterr(&ctx);
267
+ }
268
+
269
+ if (ndt_itemsize(type) != view->itemsize) {
270
+ PyErr_SetString(PyExc_RuntimeError,
271
+ "mismatch between computed itemsize and buffer itemsize");
272
+ ndt_del(type);
273
+ return NULL;
274
+ }
275
+
276
+ for (i=view->ndim-1, t=type; i>=0; i--, type=t) {
277
+ shape = view->shape[i];
278
+ step = view->strides[i] / view->itemsize;
279
+ t = ndt_fixed_dim(type, shape, step, &ctx);
280
+ if (t == NULL) {
281
+ return seterr(&ctx);
282
+ }
283
+ }
284
+
285
+ return Ndt_FromType(t);
286
+ }
287
+
288
+ static MemoryBlockObject *
289
+ mblock_from_buffer(PyObject *obj)
290
+ {
291
+ MemoryBlockObject *self;
292
+
293
+ self = mblock_alloc();
294
+ if (self == NULL) {
295
+ return NULL;
296
+ }
297
+
298
+ self->view = ndt_calloc(1, sizeof *self->view);
299
+ if (self->view == NULL) {
300
+ Py_DECREF(self);
301
+ return NULL;
302
+ }
303
+
304
+ if (PyObject_GetBuffer(obj, self->view, PyBUF_FULL_RO) < 0) {
305
+ Py_DECREF(self);
306
+ return NULL;
307
+ }
308
+
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
+ self->type = type_from_buffer(self->view);
319
+ if (self->type == NULL) {
320
+ Py_DECREF(self);
321
+ return NULL;
322
+ }
323
+
324
+ self->xnd = ndt_calloc(1, sizeof *self->xnd);
325
+ if (self->xnd == NULL) {
326
+ Py_DECREF(self);
327
+ return NULL;
328
+ }
329
+
330
+ self->xnd->flags = 0;
331
+ self->xnd->master.bitmap.data = NULL;
332
+ self->xnd->master.bitmap.size = 0;
333
+ self->xnd->master.bitmap.next = NULL;
334
+ self->xnd->master.index = 0;
335
+ self->xnd->master.type = CONST_NDT(self->type);
336
+ self->xnd->master.ptr = self->view->buf;
337
+
338
+ return self;
339
+ }
340
+
341
+ static MemoryBlockObject *
342
+ mblock_from_buffer_and_type(PyObject *obj, PyObject *type)
343
+ {
344
+ MemoryBlockObject *self;
345
+
346
+ if (!Ndt_Check(type)) {
347
+ PyErr_SetString(PyExc_TypeError, "expected ndt object");
348
+ return NULL;
349
+ }
350
+
351
+ self = mblock_alloc();
352
+ if (self == NULL) {
353
+ return NULL;
354
+ }
355
+
356
+ self->view = ndt_calloc(1, sizeof *self->view);
357
+ if (self->view == NULL) {
358
+ Py_DECREF(self);
359
+ return NULL;
360
+ }
361
+
362
+ if (PyObject_GetBuffer(obj, self->view, PyBUF_FULL_RO) < 0) {
363
+ Py_DECREF(self);
364
+ return NULL;
365
+ }
366
+
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");
372
+ Py_DECREF(self);
373
+ return NULL;
374
+ }
375
+
376
+ Py_INCREF(type);
377
+ self->type = type;
378
+
379
+ self->xnd = ndt_calloc(1, sizeof *self->xnd);
380
+ if (self->xnd == NULL) {
381
+ Py_DECREF(self);
382
+ return NULL;
383
+ }
384
+
385
+ self->xnd->flags = 0;
386
+ self->xnd->master.bitmap.data = NULL;
387
+ self->xnd->master.bitmap.size = 0;
388
+ self->xnd->master.bitmap.next = NULL;
389
+ self->xnd->master.index = 0;
390
+ self->xnd->master.type = CONST_NDT(self->type);
391
+ self->xnd->master.ptr = self->view->buf;
392
+
393
+ return self;
394
+ }
395
+
396
+
397
+ static PyTypeObject MemoryBlock_Type = {
398
+ PyVarObject_HEAD_INIT(NULL, 0)
399
+ .tp_name = "_xnd.memblock",
400
+ .tp_basicsize = sizeof(MemoryBlockObject),
401
+ .tp_dealloc = (destructor)mblock_dealloc,
402
+ .tp_hash = PyObject_HashNotImplemented,
403
+ .tp_getattro = PyObject_GenericGetAttr,
404
+ .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,
405
+ .tp_traverse = (traverseproc)mblock_traverse
406
+ };
407
+
408
+
409
+ /****************************************************************************/
410
+ /* MemoryBlock Object Initialization */
411
+ /****************************************************************************/
412
+
413
+ static void
414
+ _strncpy(char *dest, const void *src, size_t len, size_t size)
415
+ {
416
+ assert (len <= size);
417
+ memcpy(dest, src, len);
418
+ memset(dest+len, '\0', size-len);
419
+ }
420
+
421
+ static int64_t
422
+ u8_skip_trailing_zero(const uint8_t *ptr, int64_t codepoints)
423
+ {
424
+ int64_t i;
425
+
426
+ for (i=codepoints-1; i >= 0; i--)
427
+ if (ptr[i] != 0)
428
+ return i+1;
429
+
430
+ return 0;
431
+ }
432
+
433
+ static int64_t
434
+ u16_skip_trailing_zero(const uint16_t *ptr, int64_t codepoints)
435
+ {
436
+ int64_t i;
437
+
438
+ for (i=codepoints-1; i >= 0; i--)
439
+ if (ptr[i] != 0)
440
+ return i+1;
441
+
442
+ return 0;
443
+ }
444
+
445
+ static int64_t
446
+ u32_skip_trailing_zero(const uint32_t *ptr, int64_t codepoints)
447
+ {
448
+ int64_t i;
449
+
450
+ for (i=codepoints-1; i >= 0; i--)
451
+ if (ptr[i] != 0)
452
+ return i+1;
453
+
454
+ return 0;
455
+ }
456
+
457
+ static int64_t
458
+ get_int(PyObject *v, int64_t min, int64_t max)
459
+ {
460
+ PyObject *tmp;
461
+ int64_t x;
462
+
463
+ tmp = PyNumber_Index(v);
464
+ if (tmp == NULL) {
465
+ return -1;
466
+ }
467
+
468
+ x = PyLong_AsLongLong(tmp);
469
+ Py_DECREF(tmp);
470
+
471
+ if (x == -1 && PyErr_Occurred()) {
472
+ return -1;
473
+ }
474
+
475
+ if (x < min || x > max) {
476
+ PyErr_Format(PyExc_ValueError,
477
+ "out of range: %" PRIi64, x);
478
+ return -1;
479
+ }
480
+
481
+ return x;
482
+ }
483
+
484
+ static uint64_t
485
+ get_uint(PyObject *v, uint64_t max)
486
+ {
487
+ PyObject *tmp;
488
+ unsigned long long x;
489
+
490
+ tmp = PyNumber_Index(v);
491
+ if (tmp == NULL) {
492
+ return max;
493
+ }
494
+
495
+ x = PyLong_AsUnsignedLongLong(tmp);
496
+ Py_DECREF(tmp);
497
+
498
+ if (x == (unsigned long long)-1 && PyErr_Occurred()) {
499
+ return max;
500
+ }
501
+
502
+ if (x > max) {
503
+ PyErr_Format(PyExc_ValueError,
504
+ "out of range: %" PRIu64, x);
505
+ return max;
506
+ }
507
+
508
+ return x;
509
+ }
510
+
511
+ static int
512
+ mblock_init(xnd_t * const x, PyObject *v)
513
+ {
514
+ NDT_STATIC_CONTEXT(ctx);
515
+ const ndt_t * const t = x->type;
516
+
517
+ if (!check_invariants(t)) {
518
+ return -1;
519
+ }
520
+
521
+ if (ndt_is_abstract(t)) {
522
+ PyErr_SetString(PyExc_TypeError, "xnd has abstract type");
523
+ return -1;
524
+ }
525
+
526
+ /* Set missing value. */
527
+ if (ndt_is_optional(t)) {
528
+ if (t->ndim > 0) {
529
+ PyErr_SetString(PyExc_NotImplementedError,
530
+ "optional dimensions are not implemented");
531
+ return -1;
532
+ }
533
+
534
+ if (v == Py_None) {
535
+ xnd_set_na(x);
536
+ return 0;
537
+ }
538
+
539
+ xnd_set_valid(x);
540
+ }
541
+
542
+ switch (t->tag) {
543
+ case FixedDim: {
544
+ const int64_t shape = t->FixedDim.shape;
545
+ int64_t i;
546
+
547
+ if (!PyList_Check(v)) {
548
+ PyErr_Format(PyExc_TypeError,
549
+ "xnd: expected list, not '%.200s'", Py_TYPE(v)->tp_name);
550
+ return -1;
551
+ }
552
+
553
+ if (PyList_GET_SIZE(v) != shape) {
554
+ PyErr_Format(PyExc_ValueError,
555
+ "xnd: expected list with size %" PRIi64, shape);
556
+ return -1;
557
+ }
558
+
559
+ for (i = 0; i < shape; i++) {
560
+ xnd_t next = xnd_fixed_dim_next(x, i);
561
+ if (mblock_init(&next, PyList_GET_ITEM(v, i)) < 0) {
562
+ return -1;
563
+ }
564
+ }
565
+
566
+ return 0;
567
+ }
568
+
569
+ case VarDim: {
570
+ int64_t start, step, shape;
571
+ int64_t i;
572
+
573
+ if (!PyList_Check(v)) {
574
+ PyErr_Format(PyExc_TypeError,
575
+ "xnd: expected list, not '%.200s'", Py_TYPE(v)->tp_name);
576
+ return -1;
577
+ }
578
+
579
+ shape = ndt_var_indices(&start, &step, t, x->index, &ctx);
580
+ if (shape < 0) {
581
+ return seterr_int(&ctx);
582
+ }
583
+
584
+ if (PyList_GET_SIZE(v) != shape) {
585
+ PyErr_Format(PyExc_ValueError,
586
+ "xnd: expected list with size %" PRIi64, shape);
587
+ return -1;
588
+ }
589
+
590
+ for (i = 0; i < shape; i++) {
591
+ xnd_t next = xnd_var_dim_next(x, start, step, i);
592
+ if (mblock_init(&next, PyList_GET_ITEM(v, i)) < 0) {
593
+ return -1;
594
+ }
595
+ }
596
+
597
+ return 0;
598
+ }
599
+
600
+ case Tuple: {
601
+ const int64_t shape = t->Tuple.shape;
602
+ int64_t i;
603
+
604
+ if (!PyTuple_Check(v)) {
605
+ PyErr_Format(PyExc_TypeError,
606
+ "xnd: expected tuple, not '%.200s'", Py_TYPE(v)->tp_name);
607
+ return -1;
608
+ }
609
+
610
+ if (PyTuple_GET_SIZE(v) != shape) {
611
+ PyErr_Format(PyExc_ValueError,
612
+ "xnd: expected tuple with size %" PRIi64, shape);
613
+ return -1;
614
+ }
615
+
616
+ for (i = 0; i < shape; i++) {
617
+ xnd_t next = xnd_tuple_next(x, i, &ctx);
618
+ if (next.ptr == NULL) {
619
+ return seterr_int(&ctx);
620
+ }
621
+
622
+ if (mblock_init(&next, PyTuple_GET_ITEM(v, i)) < 0) {
623
+ return -1;
624
+ }
625
+ }
626
+
627
+ return 0;
628
+ }
629
+
630
+ case Record: {
631
+ const int64_t shape = t->Record.shape;
632
+ PyObject *tmp;
633
+ int64_t i;
634
+ int ret;
635
+
636
+ if (!PyDict_Check(v)) {
637
+ PyErr_Format(PyExc_TypeError,
638
+ "xnd: expected dict, not '%.200s'", Py_TYPE(v)->tp_name);
639
+ return -1;
640
+ }
641
+
642
+ if (PyDict_Size(v) != shape) {
643
+ PyErr_Format(PyExc_ValueError,
644
+ "xnd: expected dict with size %" PRIi64, shape);
645
+ return -1;
646
+ }
647
+
648
+ for (i = 0; i < shape; i++) {
649
+ xnd_t next = xnd_record_next(x, i, &ctx);
650
+ if (next.ptr == NULL) {
651
+ return seterr_int(&ctx);
652
+ }
653
+
654
+ tmp = PyMapping_GetItemString(v, t->Record.names[i]);
655
+ if (tmp == NULL) {
656
+ if (!PyErr_Occurred()) {
657
+ PyErr_Format(PyExc_ValueError,
658
+ "xnd: key not found %s", t->Record.names[i]);
659
+ }
660
+ return -1;
661
+ }
662
+
663
+ ret = mblock_init(&next, tmp);
664
+ Py_DECREF(tmp);
665
+ if (ret < 0) {
666
+ return -1;
667
+ }
668
+ }
669
+
670
+ return 0;
671
+ }
672
+
673
+ case Ref: {
674
+ xnd_t next = xnd_ref_next(x, &ctx);
675
+ if (next.ptr == NULL) {
676
+ return seterr_int(&ctx);
677
+ }
678
+
679
+ return mblock_init(&next, v);
680
+ }
681
+
682
+ case Constr: {
683
+ xnd_t next = xnd_constr_next(x, &ctx);
684
+ if (next.ptr == NULL) {
685
+ return seterr_int(&ctx);
686
+ }
687
+
688
+ return mblock_init(&next, v);
689
+ }
690
+
691
+ case Nominal: {
692
+ xnd_t next = xnd_nominal_next(x, &ctx);
693
+ if (next.ptr == NULL) {
694
+ return seterr_int(&ctx);
695
+ }
696
+
697
+ if (t->Nominal.meth->init != NULL) {
698
+ if (!t->Nominal.meth->init(&next, v, &ctx)) {
699
+ return -1;
700
+ }
701
+ return 0;
702
+ }
703
+
704
+ int ret = mblock_init(&next, v);
705
+ if (ret < 0) {
706
+ return ret;
707
+ }
708
+
709
+ if (t->Nominal.meth->constraint != NULL &&
710
+ !t->Nominal.meth->constraint(&next, &ctx)) {
711
+ return seterr_int(&ctx);
712
+ }
713
+
714
+ return ret;
715
+ }
716
+
717
+ case Bool: {
718
+ int tmp;
719
+ bool b;
720
+
721
+ if (v == Py_None) {
722
+ PyErr_SetString(PyExc_ValueError,
723
+ "assigning None to memory block with non-optional type");
724
+ return -1;
725
+ }
726
+
727
+ tmp = PyObject_IsTrue(v);
728
+ if (tmp < 0) {
729
+ return -1;
730
+ }
731
+ b = (bool)tmp;
732
+
733
+ PACK_SINGLE(x->ptr, b, bool, t->flags);
734
+ return 0;
735
+ }
736
+
737
+ case Int8: {
738
+ int8_t tmp = (int8_t)get_int(v, INT8_MIN, INT8_MAX);
739
+ if (tmp == -1 && PyErr_Occurred()) {
740
+ return -1;
741
+ }
742
+ PACK_SINGLE(x->ptr, tmp, int8_t, t->flags);
743
+ return 0;
744
+ }
745
+
746
+ case Int16: {
747
+ int16_t tmp = (int16_t)get_int(v, INT16_MIN, INT16_MAX);
748
+ if (tmp == -1 && PyErr_Occurred()) {
749
+ return -1;
750
+ }
751
+ PACK_SINGLE(x->ptr, tmp, int16_t, t->flags);
752
+ return 0;
753
+ }
754
+
755
+ case Int32: {
756
+ int32_t tmp = (int32_t)get_int(v, INT32_MIN, INT32_MAX);
757
+ if (tmp == -1 && PyErr_Occurred()) {
758
+ return -1;
759
+ }
760
+ PACK_SINGLE(x->ptr, tmp, int32_t, t->flags);
761
+ return 0;
762
+ }
763
+
764
+ case Int64: {
765
+ int64_t tmp = get_int(v, INT64_MIN, INT64_MAX);
766
+ if (tmp == -1 && PyErr_Occurred()) {
767
+ return -1;
768
+ }
769
+ PACK_SINGLE(x->ptr, tmp, int64_t, t->flags);
770
+ return 0;
771
+ }
772
+
773
+ case Uint8: {
774
+ uint8_t tmp = (uint8_t)get_uint(v, UINT8_MAX);
775
+ if (tmp == UINT8_MAX && PyErr_Occurred()) {
776
+ return -1;
777
+ }
778
+ PACK_SINGLE(x->ptr, tmp, uint8_t, t->flags);
779
+ return 0;
780
+ }
781
+
782
+ case Uint16: {
783
+ uint16_t tmp = (uint16_t)get_uint(v, UINT16_MAX);
784
+ if (tmp == UINT16_MAX && PyErr_Occurred()) {
785
+ return -1;
786
+ }
787
+ PACK_SINGLE(x->ptr, tmp, uint16_t, t->flags);
788
+ return 0;
789
+ }
790
+
791
+ case Uint32: {
792
+ uint32_t tmp = (uint32_t)get_uint(v, UINT32_MAX);
793
+ if (tmp == UINT32_MAX && PyErr_Occurred()) {
794
+ return -1;
795
+ }
796
+ PACK_SINGLE(x->ptr, tmp, uint32_t, t->flags);
797
+ return 0;
798
+ }
799
+
800
+ case Uint64: {
801
+ uint64_t tmp = get_uint(v, UINT64_MAX);
802
+ if (tmp == UINT64_MAX && PyErr_Occurred()) {
803
+ return -1;
804
+ }
805
+ PACK_SINGLE(x->ptr, tmp, uint64_t, t->flags);
806
+ return 0;
807
+ }
808
+
809
+ case Float16: {
810
+ #if PY_VERSION_HEX >= 0x03060000
811
+ double tmp = PyFloat_AsDouble(v);
812
+ if (tmp == -1 && PyErr_Occurred()) {
813
+ return -1;
814
+ }
815
+ return _PyFloat_Pack2(tmp, (unsigned char *)x->ptr, le(t->flags));
816
+ #else
817
+ PyErr_SetString(PyExc_NotImplementedError,
818
+ "half-float not implemented in Python versions < 3.6");
819
+ return -1;
820
+ #endif
821
+ }
822
+
823
+ case Float32: {
824
+ double tmp = PyFloat_AsDouble(v);
825
+ if (tmp == -1 && PyErr_Occurred()) {
826
+ return -1;
827
+ }
828
+ return _PyFloat_Pack4(tmp, (unsigned char *)x->ptr, le(t->flags));
829
+ }
830
+
831
+ case Float64: {
832
+ double tmp = PyFloat_AsDouble(v);
833
+ if (tmp == -1 && PyErr_Occurred()) {
834
+ return -1;
835
+ }
836
+ return _PyFloat_Pack8(tmp, (unsigned char *)x->ptr, le(t->flags));
837
+ }
838
+
839
+ case Complex32: {
840
+ #if PY_VERSION_HEX >= 0x03060000
841
+ Py_complex c = PyComplex_AsCComplex(v);
842
+ if (c.real == -1.0 && PyErr_Occurred()) {
843
+ return -1;
844
+ }
845
+ if (_PyFloat_Pack2(c.real, (unsigned char *)x->ptr, le(t->flags)) < 0) {
846
+ return -1;
847
+ }
848
+ return _PyFloat_Pack2(c.imag, (unsigned char *)(x->ptr+2), le(t->flags));
849
+ #else
850
+ PyErr_SetString(PyExc_NotImplementedError,
851
+ "half-float not implemented in Python versions < 3.6");
852
+ return -1;
853
+ #endif
854
+ }
855
+
856
+ case Complex64: {
857
+ Py_complex c = PyComplex_AsCComplex(v);
858
+ if (c.real == -1.0 && PyErr_Occurred()) {
859
+ return -1;
860
+ }
861
+ if (_PyFloat_Pack4(c.real, (unsigned char *)x->ptr, le(t->flags)) < 0) {
862
+ return -1;
863
+ }
864
+ return _PyFloat_Pack4(c.imag, (unsigned char *)(x->ptr+4), le(t->flags));
865
+ }
866
+
867
+ case Complex128: {
868
+ Py_complex c = PyComplex_AsCComplex(v);
869
+ if (c.real == -1.0 && PyErr_Occurred()) {
870
+ return -1;
871
+ }
872
+ if (_PyFloat_Pack8(c.real, (unsigned char *)x->ptr, le(t->flags)) < 0) {
873
+ return -1;
874
+ }
875
+ return _PyFloat_Pack8(c.imag, (unsigned char *)(x->ptr+8), le(t->flags));
876
+ }
877
+
878
+ case FixedString: {
879
+ int64_t codepoints = t->FixedString.size;
880
+ int64_t len;
881
+
882
+ if (!PyUnicode_Check(v)) {
883
+ PyErr_SetString(PyExc_TypeError, "expected unicode object");
884
+ return -1;
885
+ }
886
+
887
+ if (PyUnicode_READY(v) < 0) {
888
+ return -1;
889
+ }
890
+
891
+ switch (t->FixedString.encoding) {
892
+ case Ascii: {
893
+ if (!PyUnicode_IS_ASCII(v)) {
894
+ PyErr_SetString(PyExc_ValueError,
895
+ "expected ascii string");
896
+ return -1;
897
+ }
898
+
899
+ len = PyUnicode_GET_LENGTH(v);
900
+
901
+ if (len > t->datasize) {
902
+ PyErr_Format(PyExc_ValueError,
903
+ "maximum string size (in bytes) is %" PRIi64, codepoints);
904
+ return -1;
905
+ }
906
+
907
+ _strncpy(x->ptr, PyUnicode_1BYTE_DATA(v), (size_t)len, (size_t)t->datasize);
908
+ return 0;
909
+ }
910
+
911
+ case Utf8: {
912
+ if (PyUnicode_KIND(v) != PyUnicode_1BYTE_KIND) {
913
+ PyErr_SetString(PyExc_ValueError,
914
+ "expected utf8 string");
915
+ return -1;
916
+ }
917
+
918
+ len = PyUnicode_GET_LENGTH(v);
919
+ if (len > t->datasize) {
920
+ PyErr_Format(PyExc_ValueError,
921
+ "maximum string size (in UTF-8 code points) is %" PRIi64, codepoints);
922
+ return -1;
923
+ }
924
+
925
+ _strncpy(x->ptr, PyUnicode_1BYTE_DATA(v), (size_t)len, (size_t)t->datasize);
926
+ return 0;
927
+ }
928
+
929
+ case Utf16: {
930
+ PyObject *b = PyUnicode_AsUTF16String(v);
931
+ if (b == NULL) {
932
+ return -1;
933
+ }
934
+
935
+ len = PyBytes_GET_SIZE(b);
936
+
937
+ if (len-2 > t->datasize) {
938
+ PyErr_Format(PyExc_ValueError,
939
+ "maximum string size (in UTF-16 code points) is %" PRIi64, codepoints);
940
+ return -1;
941
+ }
942
+
943
+ /* skip byte order mark */
944
+ assert(len >= 2);
945
+
946
+ _strncpy(x->ptr, PyBytes_AS_STRING(b)+2, (size_t)(len-2), (size_t)t->datasize);
947
+ Py_DECREF(b);
948
+
949
+ return 0;
950
+ }
951
+
952
+ case Utf32: {
953
+ PyObject *b = PyUnicode_AsUTF32String(v);
954
+ if (b == NULL) {
955
+ return -1;
956
+ }
957
+
958
+ len = PyBytes_GET_SIZE(b);
959
+
960
+ if (len-4 > t->datasize) {
961
+ PyErr_Format(PyExc_ValueError,
962
+ "maximum string size (in UTF-32 code points) is %" PRIi64, codepoints);
963
+ return -1;
964
+ }
965
+
966
+ /* skip byte order mark */
967
+ assert(len >= 4);
968
+
969
+ _strncpy(x->ptr, PyBytes_AS_STRING(b)+4, (size_t)(len-4), (size_t)t->datasize);
970
+ Py_DECREF(b);
971
+
972
+ return 0;
973
+ }
974
+
975
+ case Ucs2:
976
+ PyErr_SetString(PyExc_NotImplementedError,
977
+ "ucs2 encoding not implemented");
978
+ return -1;
979
+
980
+ default:
981
+ PyErr_SetString(PyExc_RuntimeError, "invalid encoding");
982
+ return -1;
983
+ }
984
+ }
985
+
986
+ case FixedBytes: {
987
+ int64_t size = t->FixedBytes.size;
988
+ int64_t len;
989
+
990
+ if (!PyBytes_Check(v)) {
991
+ PyErr_SetString(PyExc_TypeError, "expected bytes object");
992
+ return -1;
993
+ }
994
+
995
+ len = PyBytes_GET_SIZE(v);
996
+
997
+ if (len > size) {
998
+ PyErr_Format(PyExc_ValueError,
999
+ "maximum bytes size is %" PRIi64, size);
1000
+ return -1;
1001
+ }
1002
+
1003
+ _strncpy(x->ptr, PyBytes_AS_STRING(v), (size_t)len, (size_t)size);
1004
+
1005
+ return 0;
1006
+ }
1007
+
1008
+ case String: {
1009
+ Py_ssize_t size;
1010
+ const char *cp;
1011
+ char *s;
1012
+
1013
+ cp = PyUnicode_AsUTF8AndSize(v, &size);
1014
+ if (cp == NULL) {
1015
+ return -1;
1016
+ }
1017
+
1018
+ s = ndt_strdup(cp, &ctx);
1019
+ if (s == NULL) {
1020
+ return seterr_int(&ctx);
1021
+ }
1022
+
1023
+ if (XND_POINTER_DATA(x->ptr)) {
1024
+ ndt_free(XND_POINTER_DATA(x->ptr));
1025
+ }
1026
+
1027
+ XND_POINTER_DATA(x->ptr) = s;
1028
+ return 0;
1029
+ }
1030
+
1031
+ case Bytes: {
1032
+ Py_ssize_t size;
1033
+ char *cp;
1034
+ char *s;
1035
+
1036
+ if (PyBytes_AsStringAndSize(v, &cp, &size) < 0) {
1037
+ return -1;
1038
+ }
1039
+
1040
+ s = ndt_aligned_calloc(t->Bytes.target_align, size);
1041
+ if (s == NULL) {
1042
+ PyErr_NoMemory();
1043
+ return -1;
1044
+ }
1045
+
1046
+ memcpy(s, cp, size);
1047
+
1048
+ if (XND_BYTES_DATA(x->ptr)) {
1049
+ ndt_aligned_free(XND_BYTES_DATA(x->ptr));
1050
+ }
1051
+
1052
+ XND_BYTES_SIZE(x->ptr) = size;
1053
+ XND_BYTES_DATA(x->ptr) = (uint8_t *)s;
1054
+ return 0;
1055
+ }
1056
+
1057
+ case Categorical: {
1058
+ int64_t k;
1059
+
1060
+ if (PyBool_Check(v)) {
1061
+ int tmp = PyObject_IsTrue(v);
1062
+ if (tmp < 0) {
1063
+ return -1;
1064
+ }
1065
+
1066
+ for (k = 0; k < t->Categorical.ntypes; k++) {
1067
+ if (t->Categorical.types[k].tag == ValBool &&
1068
+ tmp == t->Categorical.types[k].ValBool) {
1069
+ PACK_SINGLE(x->ptr, k, int64_t, t->flags);
1070
+ return 0;
1071
+ }
1072
+ }
1073
+ goto not_found;
1074
+ }
1075
+
1076
+ else if (PyLong_Check(v)) {
1077
+ int64_t tmp = get_int(v, INT64_MIN, INT64_MAX);
1078
+ if (tmp == -1 && PyErr_Occurred()) {
1079
+ return -1;
1080
+ }
1081
+
1082
+ for (k = 0; k < t->Categorical.ntypes; k++) {
1083
+ if (t->Categorical.types[k].tag == ValInt64 &&
1084
+ tmp == t->Categorical.types[k].ValInt64) {
1085
+ PACK_SINGLE(x->ptr, k, int64_t, t->flags);
1086
+ return 0;
1087
+ }
1088
+ }
1089
+ goto not_found;
1090
+ }
1091
+
1092
+ else if (PyFloat_Check(v)) {
1093
+ double tmp = PyFloat_AsDouble(v);
1094
+ if (tmp == -1 && PyErr_Occurred()) {
1095
+ return -1;
1096
+ }
1097
+
1098
+ for (k = 0; k < t->Categorical.ntypes; k++) {
1099
+ /* XXX: DBL_EPSILON? */
1100
+ if (t->Categorical.types[k].tag == ValFloat64 &&
1101
+ tmp == t->Categorical.types[k].ValFloat64) {
1102
+ PACK_SINGLE(x->ptr, k, int64_t, t->flags);
1103
+ return 0;
1104
+ }
1105
+ }
1106
+ goto not_found;
1107
+ }
1108
+
1109
+ else if (PyUnicode_Check(v)) {
1110
+ const char *tmp = PyUnicode_AsUTF8(v);
1111
+ if (tmp == NULL) {
1112
+ return -1;
1113
+ }
1114
+
1115
+ for (k = 0; k < t->Categorical.ntypes; k++) {
1116
+ if (t->Categorical.types[k].tag == ValString &&
1117
+ strcmp(tmp, t->Categorical.types[k].ValString) == 0) {
1118
+ PACK_SINGLE(x->ptr, k, int64_t, t->flags);
1119
+ return 0;
1120
+ }
1121
+ }
1122
+ goto not_found;
1123
+ }
1124
+
1125
+ not_found:
1126
+ for (k = 0; k < t->Categorical.ntypes; k++) {
1127
+ if (t->Categorical.types[k].tag == ValNA) {
1128
+ PACK_SINGLE(x->ptr, k, int64_t, t->flags);
1129
+ return 0;
1130
+ }
1131
+ }
1132
+
1133
+ PyErr_Format(PyExc_ValueError, "category not found for: %.200R", v);
1134
+ return -1;
1135
+ }
1136
+
1137
+ case Char:
1138
+ PyErr_SetString(PyExc_NotImplementedError,
1139
+ "'Char' type semantics need to be defined");
1140
+ return -1;
1141
+
1142
+ /* NOT REACHED: intercepted by ndt_is_abstract(). */
1143
+ case Module: case Function:
1144
+ case AnyKind: case SymbolicDim: case EllipsisDim: case Typevar:
1145
+ case ScalarKind: case SignedKind: case UnsignedKind: case FloatKind:
1146
+ case ComplexKind: case FixedStringKind: case FixedBytesKind:
1147
+ PyErr_SetString(PyExc_RuntimeError, "unexpected abstract type");
1148
+ return -1;
1149
+ }
1150
+
1151
+ /* NOT REACHED: tags should be exhaustive */
1152
+ PyErr_SetString(PyExc_RuntimeError, "invalid type tag");
1153
+ return -1;
1154
+ }
1155
+
1156
+
1157
+ /****************************************************************************/
1158
+ /* xnd object */
1159
+ /****************************************************************************/
1160
+
1161
+ static PyTypeObject Xnd_Type;
1162
+
1163
+ static inline bool
1164
+ is_readonly(XndObject *self)
1165
+ {
1166
+ return self->mblock->view != NULL && self->mblock->view->readonly;
1167
+ }
1168
+
1169
+
1170
+ static XndObject *
1171
+ pyxnd_alloc(PyTypeObject *type)
1172
+ {
1173
+ XndObject *self;
1174
+
1175
+ self = (XndObject *)type->tp_alloc(type, 0);
1176
+ if (self == NULL) {
1177
+ return NULL;
1178
+ }
1179
+
1180
+ self->mblock = NULL;
1181
+ self->type = NULL;
1182
+ self->xnd.bitmap.data = NULL;
1183
+ self->xnd.bitmap.size = 0;
1184
+ self->xnd.bitmap.next = NULL;
1185
+ self->xnd.index = 0;
1186
+ self->xnd.type = NULL;
1187
+ self->xnd.ptr = NULL;
1188
+
1189
+ return self;
1190
+ }
1191
+
1192
+ static int
1193
+ pyxnd_traverse(XndObject *self, visitproc visit, void *arg)
1194
+ {
1195
+ Py_VISIT(self->mblock);
1196
+ Py_VISIT(self->type);
1197
+ return 0;
1198
+ }
1199
+
1200
+ static void
1201
+ pyxnd_dealloc(XndObject *self)
1202
+ {
1203
+ PyObject_GC_UnTrack(self);
1204
+ Py_CLEAR(self->mblock);
1205
+ Py_CLEAR(self->type);
1206
+ Py_TYPE(self)->tp_free(self);
1207
+ }
1208
+
1209
+ static PyObject *
1210
+ pyxnd_from_mblock(PyTypeObject *tp, MemoryBlockObject *mblock)
1211
+ {
1212
+ XndObject *self;
1213
+
1214
+ self = pyxnd_alloc(tp);
1215
+ if (self == NULL) {
1216
+ Py_DECREF(mblock);
1217
+ return NULL;
1218
+ }
1219
+
1220
+ Py_INCREF(mblock->type);
1221
+ self->mblock = mblock;
1222
+ self->type = mblock->type;
1223
+ self->xnd = mblock->xnd->master;
1224
+
1225
+ return (PyObject *)self;
1226
+ }
1227
+
1228
+ static PyObject *
1229
+ pyxnd_new(PyTypeObject *tp, PyObject *args, PyObject *kwds)
1230
+ {
1231
+ static char *kwlist[] = {"type", "value", NULL};
1232
+ PyObject *type = NULL;
1233
+ PyObject *value = NULL;
1234
+ MemoryBlockObject *mblock;
1235
+
1236
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "OO", kwlist, &type,
1237
+ &value)) {
1238
+ return NULL;
1239
+ }
1240
+
1241
+ mblock = mblock_from_typed_value(type, value);
1242
+ if (mblock == NULL) {
1243
+ return NULL;
1244
+ }
1245
+
1246
+ return pyxnd_from_mblock(tp, mblock);
1247
+ }
1248
+
1249
+ static PyObject *
1250
+ pyxnd_empty(PyTypeObject *tp, PyObject *type)
1251
+ {
1252
+ MemoryBlockObject *mblock;
1253
+
1254
+ type = Ndt_FromObject(type);
1255
+ if (type == NULL) {
1256
+ return NULL;
1257
+ }
1258
+
1259
+ mblock = mblock_empty(type);
1260
+ Py_DECREF(type);
1261
+ if (mblock == NULL) {
1262
+ return NULL;
1263
+ }
1264
+
1265
+ return pyxnd_from_mblock(tp, mblock);
1266
+ }
1267
+
1268
+ static PyObject *
1269
+ pyxnd_from_buffer(PyTypeObject *tp, PyObject *obj)
1270
+ {
1271
+ MemoryBlockObject *mblock;
1272
+
1273
+ mblock = mblock_from_buffer(obj);
1274
+ if (mblock == NULL) {
1275
+ return NULL;
1276
+ }
1277
+
1278
+ return pyxnd_from_mblock(tp, mblock);
1279
+ }
1280
+
1281
+ static PyObject *
1282
+ pyxnd_from_buffer_and_type(PyTypeObject *tp, PyObject *args, PyObject *kwds)
1283
+ {
1284
+ static char *kwlist[] = {"obj", "type", NULL};
1285
+ PyObject *obj = NULL;
1286
+ PyObject *type = NULL;
1287
+ MemoryBlockObject *mblock;
1288
+
1289
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "OO", kwlist, &obj, &type)) {
1290
+ return NULL;
1291
+ }
1292
+
1293
+ mblock = mblock_from_buffer_and_type(obj, type);
1294
+ if (mblock == NULL) {
1295
+ return NULL;
1296
+ }
1297
+
1298
+ return pyxnd_from_mblock(tp, mblock);
1299
+ }
1300
+
1301
+
1302
+ /******************************************************************************/
1303
+ /* xnd methods */
1304
+ /******************************************************************************/
1305
+
1306
+ static int
1307
+ dict_set_item(PyObject *dict, const char *k, PyObject *value)
1308
+ {
1309
+ PyObject *key;
1310
+ int ret;
1311
+
1312
+ key = PyUnicode_FromString(k);
1313
+ if (key == NULL) {
1314
+ return -1;
1315
+ }
1316
+
1317
+ ret = PyDict_SetItem(dict, key, value);
1318
+ Py_DECREF(key);
1319
+
1320
+ return ret;
1321
+ }
1322
+
1323
+ static PyObject *
1324
+ _pyxnd_value(const xnd_t * const x, const int64_t maxshape)
1325
+ {
1326
+ NDT_STATIC_CONTEXT(ctx);
1327
+ const ndt_t * const t = x->type;
1328
+
1329
+ assert(ndt_is_concrete(t));
1330
+
1331
+ /* Bitmap access needs the linear index. */
1332
+ if (xnd_is_na(x)) {
1333
+ Py_RETURN_NONE;
1334
+ }
1335
+
1336
+ switch (t->tag) {
1337
+ case FixedDim: {
1338
+ PyObject *lst, *v;
1339
+ int64_t shape, i;
1340
+
1341
+ shape = t->FixedDim.shape;
1342
+ if (shape > maxshape) {
1343
+ shape = maxshape;
1344
+ }
1345
+
1346
+ lst = list_new(shape);
1347
+ if (lst == NULL) {
1348
+ return NULL;
1349
+ }
1350
+
1351
+ for (i = 0; i < shape; i++) {
1352
+ if (i == maxshape-1) {
1353
+ PyList_SET_ITEM(lst, i, xnd_ellipsis());
1354
+ break;
1355
+ }
1356
+
1357
+ const xnd_t next = xnd_fixed_dim_next(x, i);
1358
+ v = _pyxnd_value(&next, maxshape);
1359
+ if (v == NULL) {
1360
+ Py_DECREF(lst);
1361
+ return NULL;
1362
+ }
1363
+ PyList_SET_ITEM(lst, i, v);
1364
+ }
1365
+
1366
+ return lst;
1367
+ }
1368
+
1369
+ case VarDim: {
1370
+ PyObject *lst, *v;
1371
+ int64_t start, step, shape;
1372
+ int64_t i;
1373
+
1374
+ shape = ndt_var_indices(&start, &step, t, x->index, &ctx);
1375
+ if (shape < 0) {
1376
+ return seterr(&ctx);
1377
+ }
1378
+ if (shape > maxshape) {
1379
+ shape = maxshape;
1380
+ }
1381
+
1382
+ lst = list_new(shape);
1383
+ if (lst == NULL) {
1384
+ return NULL;
1385
+ }
1386
+
1387
+ for (i = 0; i < shape; i++) {
1388
+ if (i == maxshape-1) {
1389
+ PyList_SET_ITEM(lst, i, xnd_ellipsis());
1390
+ break;
1391
+ }
1392
+
1393
+ const xnd_t next = xnd_var_dim_next(x, start, step, i);
1394
+ v = _pyxnd_value(&next, maxshape);
1395
+ if (v == NULL) {
1396
+ Py_DECREF(lst);
1397
+ return NULL;
1398
+ }
1399
+ PyList_SET_ITEM(lst, i, v);
1400
+ }
1401
+
1402
+ return lst;
1403
+ }
1404
+
1405
+ case Tuple: {
1406
+ PyObject *tuple, *v;
1407
+ int64_t shape, i;
1408
+
1409
+ shape = t->Tuple.shape;
1410
+ if (shape > maxshape) {
1411
+ shape = maxshape;
1412
+ }
1413
+
1414
+ tuple = tuple_new(shape);
1415
+ if (tuple == NULL) {
1416
+ return NULL;
1417
+ }
1418
+
1419
+ for (i = 0; i < shape; i++) {
1420
+ if (i == maxshape-1) {
1421
+ PyTuple_SET_ITEM(tuple, i, xnd_ellipsis());
1422
+ break;
1423
+ }
1424
+
1425
+ const xnd_t next = xnd_tuple_next(x, i, &ctx);
1426
+ if (next.ptr == NULL) {
1427
+ return seterr(&ctx);
1428
+ }
1429
+
1430
+ v = _pyxnd_value(&next, maxshape);
1431
+ if (v == NULL) {
1432
+ Py_DECREF(tuple);
1433
+ return NULL;
1434
+ }
1435
+ PyTuple_SET_ITEM(tuple, i, v);
1436
+ }
1437
+
1438
+ return tuple;
1439
+ }
1440
+
1441
+ case Record: {
1442
+ PyObject *dict, *v;
1443
+ int64_t shape, i;
1444
+ int ret;
1445
+
1446
+ shape = t->Record.shape;
1447
+ if (shape > maxshape) {
1448
+ shape = maxshape;
1449
+ }
1450
+
1451
+ dict = PyDict_New();
1452
+ if (dict == NULL) {
1453
+ return NULL;
1454
+ }
1455
+
1456
+ for (i = 0; i < shape; i++) {
1457
+ if (i == maxshape-1) {
1458
+ ret = PyDict_SetItem(dict, &XndEllipsisObject, &XndEllipsisObject);
1459
+ if (ret < 0) {
1460
+ Py_DECREF(dict);
1461
+ return NULL;
1462
+ }
1463
+ break;
1464
+ }
1465
+
1466
+ const xnd_t next = xnd_record_next(x, i, &ctx);
1467
+ if (next.ptr == NULL) {
1468
+ return seterr(&ctx);
1469
+ }
1470
+
1471
+ v = _pyxnd_value(&next, maxshape);
1472
+ if (v == NULL) {
1473
+ Py_DECREF(dict);
1474
+ return NULL;
1475
+ }
1476
+
1477
+ ret = dict_set_item(dict, t->Record.names[i], v);
1478
+ Py_DECREF(v);
1479
+ if (ret < 0) {
1480
+ Py_DECREF(dict);
1481
+ return NULL;
1482
+ }
1483
+ }
1484
+
1485
+ return dict;
1486
+ }
1487
+
1488
+ case Ref: {
1489
+ const xnd_t next = xnd_ref_next(x, &ctx);
1490
+ if (next.ptr == NULL) {
1491
+ return seterr(&ctx);
1492
+ }
1493
+
1494
+ return _pyxnd_value(&next, maxshape);
1495
+ }
1496
+
1497
+ case Constr: {
1498
+ const xnd_t next = xnd_constr_next(x, &ctx);
1499
+ if (next.ptr == NULL) {
1500
+ return seterr(&ctx);
1501
+ }
1502
+
1503
+ return _pyxnd_value(&next, maxshape);
1504
+ }
1505
+
1506
+ case Nominal: {
1507
+ const xnd_t next = xnd_nominal_next(x, &ctx);
1508
+ if (next.ptr == NULL) {
1509
+ return seterr(&ctx);
1510
+ }
1511
+
1512
+ if (t->Nominal.meth->repr != NULL) {
1513
+ return t->Nominal.meth->repr(&next, &ctx);
1514
+ }
1515
+
1516
+ return _pyxnd_value(&next, maxshape);
1517
+ }
1518
+
1519
+ case Bool: {
1520
+ bool tmp;
1521
+ UNPACK_SINGLE(tmp, x->ptr, bool, t->flags);
1522
+ return PyBool_FromLong(tmp);
1523
+ }
1524
+
1525
+ case Int8: {
1526
+ int8_t tmp;
1527
+ UNPACK_SINGLE(tmp, x->ptr, int8_t, t->flags);
1528
+ return PyLong_FromLong(tmp);
1529
+ }
1530
+
1531
+ case Int16: {
1532
+ int16_t tmp;
1533
+ UNPACK_SINGLE(tmp, x->ptr, int16_t, t->flags);
1534
+ return PyLong_FromLong(tmp);
1535
+ }
1536
+
1537
+ case Int32: {
1538
+ int32_t tmp;
1539
+ UNPACK_SINGLE(tmp, x->ptr, int32_t, t->flags);
1540
+ return PyLong_FromLong(tmp);
1541
+ }
1542
+
1543
+ case Int64: {
1544
+ int64_t tmp;
1545
+ UNPACK_SINGLE(tmp, x->ptr, int64_t, t->flags);
1546
+ return PyLong_FromLongLong(tmp);
1547
+ }
1548
+
1549
+ case Uint8: {
1550
+ uint8_t tmp;
1551
+ UNPACK_SINGLE(tmp, x->ptr, uint8_t, t->flags);
1552
+ return PyLong_FromUnsignedLong(tmp);
1553
+ }
1554
+
1555
+ case Uint16: {
1556
+ uint16_t tmp;
1557
+ UNPACK_SINGLE(tmp, x->ptr, uint16_t, t->flags);
1558
+ return PyLong_FromUnsignedLong(tmp);
1559
+ }
1560
+
1561
+ case Uint32: {
1562
+ uint32_t tmp;
1563
+ UNPACK_SINGLE(tmp, x->ptr, uint32_t, t->flags);
1564
+ return PyLong_FromUnsignedLong(tmp);
1565
+ }
1566
+
1567
+ case Uint64: {
1568
+ uint64_t tmp;
1569
+ UNPACK_SINGLE(tmp, x->ptr, uint64_t, t->flags);
1570
+ return PyLong_FromUnsignedLongLong(tmp);
1571
+ }
1572
+
1573
+ case Float16: {
1574
+ #if PY_VERSION_HEX >= 0x03060000
1575
+ double tmp = _PyFloat_Unpack2((unsigned char *)x->ptr, le(t->flags));
1576
+ if (tmp == -1.0 && PyErr_Occurred()) {
1577
+ return NULL;
1578
+ }
1579
+ return PyFloat_FromDouble(tmp);
1580
+ #else
1581
+ PyErr_SetString(PyExc_NotImplementedError,
1582
+ "half-float not implemented in Python versions < 3.6");
1583
+ return NULL;
1584
+ #endif
1585
+ }
1586
+
1587
+ case Float32: {
1588
+ double tmp = _PyFloat_Unpack4((unsigned char *)x->ptr, le(t->flags));
1589
+ if (tmp == -1.0 && PyErr_Occurred()) {
1590
+ return NULL;
1591
+ }
1592
+ return PyFloat_FromDouble(tmp);
1593
+ }
1594
+
1595
+ case Float64: {
1596
+ double tmp = _PyFloat_Unpack8((unsigned char *)x->ptr, le(t->flags));
1597
+ if (tmp == -1.0 && PyErr_Occurred()) {
1598
+ return NULL;
1599
+ }
1600
+ return PyFloat_FromDouble(tmp);
1601
+ }
1602
+
1603
+ case Complex32: {
1604
+ #if PY_VERSION_HEX >= 0x03060000
1605
+ Py_complex c;
1606
+ c.real = _PyFloat_Unpack2((unsigned char *)x->ptr, le(t->flags));
1607
+ if (c.real == -1.0 && PyErr_Occurred()) {
1608
+ return NULL;
1609
+ }
1610
+ c.imag = _PyFloat_Unpack2((unsigned char *)x->ptr+2, le(t->flags));
1611
+ if (c.imag == -1.0 && PyErr_Occurred()) {
1612
+ return NULL;
1613
+ }
1614
+ return PyComplex_FromCComplex(c);
1615
+ #else
1616
+ PyErr_SetString(PyExc_NotImplementedError,
1617
+ "half-float not implemented in Python versions < 3.6");
1618
+ return NULL;
1619
+ #endif
1620
+ }
1621
+
1622
+ case Complex64: {
1623
+ Py_complex c;
1624
+ c.real = _PyFloat_Unpack4((unsigned char *)x->ptr, le(t->flags));
1625
+ if (c.real == -1.0 && PyErr_Occurred()) {
1626
+ return NULL;
1627
+ }
1628
+ c.imag = _PyFloat_Unpack4((unsigned char *)x->ptr+4, le(t->flags));
1629
+ if (c.imag == -1.0 && PyErr_Occurred()) {
1630
+ return NULL;
1631
+ }
1632
+ return PyComplex_FromCComplex(c);
1633
+ }
1634
+
1635
+ case Complex128: {
1636
+ Py_complex c;
1637
+ c.real = _PyFloat_Unpack8((unsigned char *)x->ptr, le(t->flags));
1638
+ if (c.real == -1.0 && PyErr_Occurred()) {
1639
+ return NULL;
1640
+ }
1641
+ c.imag = _PyFloat_Unpack8((unsigned char *)x->ptr+8, le(t->flags));
1642
+ if (c.imag == -1.0 && PyErr_Occurred()) {
1643
+ return NULL;
1644
+ }
1645
+ return PyComplex_FromCComplex(c);
1646
+ }
1647
+
1648
+ case FixedString: {
1649
+ int64_t codepoints = t->FixedString.size;
1650
+
1651
+ switch (t->FixedString.encoding) {
1652
+ case Ascii:
1653
+ codepoints = u8_skip_trailing_zero((uint8_t *)x->ptr, codepoints);
1654
+ return unicode_from_kind_and_data(PyUnicode_1BYTE_KIND, x->ptr, codepoints);
1655
+
1656
+ case Utf8:
1657
+ codepoints = u8_skip_trailing_zero((uint8_t *)x->ptr, codepoints);
1658
+ return unicode_from_kind_and_data(PyUnicode_1BYTE_KIND, x->ptr, codepoints);
1659
+
1660
+ case Utf16:
1661
+ codepoints = u16_skip_trailing_zero((uint16_t *)x->ptr, codepoints);
1662
+ return unicode_from_kind_and_data(PyUnicode_2BYTE_KIND, x->ptr, codepoints);
1663
+
1664
+ case Utf32:
1665
+ codepoints = u32_skip_trailing_zero((uint32_t *)x->ptr, codepoints);
1666
+ return unicode_from_kind_and_data(PyUnicode_4BYTE_KIND, x->ptr, codepoints);
1667
+
1668
+ case Ucs2:
1669
+ PyErr_SetString(PyExc_NotImplementedError,
1670
+ "ucs2 encoding not implemented");
1671
+ return NULL;
1672
+
1673
+ default:
1674
+ PyErr_SetString(PyExc_RuntimeError, "invalid encoding");
1675
+ return NULL;
1676
+ }
1677
+ }
1678
+
1679
+ case FixedBytes: {
1680
+ return bytes_from_string_and_size(x->ptr, t->FixedBytes.size);
1681
+ }
1682
+
1683
+ case String: {
1684
+ const char *s = XND_POINTER_DATA(x->ptr);
1685
+ Py_ssize_t size = s ? strlen(s) : 0;
1686
+
1687
+ return PyUnicode_FromStringAndSize(s, size);
1688
+ }
1689
+
1690
+ case Bytes: {
1691
+ char *s = (char *)XND_BYTES_DATA(x->ptr);
1692
+ int64_t size = XND_BYTES_SIZE(x->ptr);
1693
+
1694
+ return bytes_from_string_and_size(s, size);
1695
+ }
1696
+
1697
+ case Categorical: {
1698
+ int64_t k;
1699
+
1700
+ UNPACK_SINGLE(k, x->ptr, int64_t, t->flags);
1701
+
1702
+ switch (t->Categorical.types[k].tag) {
1703
+ case ValBool: {
1704
+ bool tmp = t->Categorical.types[k].ValBool;
1705
+ return PyBool_FromLong(tmp);
1706
+ }
1707
+
1708
+ case ValInt64: {
1709
+ int64_t tmp = t->Categorical.types[k].ValInt64;
1710
+ return PyLong_FromLongLong(tmp);
1711
+ }
1712
+
1713
+ case ValFloat64: {
1714
+ double tmp = t->Categorical.types[k].ValFloat64;
1715
+ return PyFloat_FromDouble(tmp);
1716
+ }
1717
+
1718
+ case ValString: {
1719
+ const char *tmp = t->Categorical.types[k].ValString;
1720
+ return PyUnicode_FromString(tmp);
1721
+ }
1722
+
1723
+ case ValNA: {
1724
+ Py_RETURN_NONE;
1725
+ }
1726
+
1727
+ }
1728
+
1729
+ PyErr_SetString(PyExc_RuntimeError, "unexpected category tag");
1730
+ return NULL;
1731
+ }
1732
+
1733
+ case Char:
1734
+ PyErr_SetString(PyExc_NotImplementedError,
1735
+ "'Char' type semantics need to be defined");
1736
+ return NULL;
1737
+
1738
+ /* NOT REACHED: intercepted by ndt_is_abstract(). */
1739
+ case Module: case Function:
1740
+ case AnyKind: case SymbolicDim: case EllipsisDim: case Typevar:
1741
+ case ScalarKind: case SignedKind: case UnsignedKind: case FloatKind:
1742
+ case ComplexKind: case FixedStringKind: case FixedBytesKind:
1743
+ PyErr_SetString(PyExc_RuntimeError, "unexpected abstract type");
1744
+ return NULL;
1745
+ }
1746
+
1747
+ /* NOT REACHED: tags should be exhaustive */
1748
+ PyErr_SetString(PyExc_RuntimeError, "invalid type tag");
1749
+ return NULL;
1750
+ }
1751
+
1752
+
1753
+ /******************************************************************************/
1754
+ /* Indexing and slicing */
1755
+ /******************************************************************************/
1756
+
1757
+ static PyObject *
1758
+ pyxnd_view_move_type(const XndObject *src, xnd_t *x)
1759
+ {
1760
+ XndObject *view;
1761
+ PyObject *type;
1762
+
1763
+ type = Ndt_MoveSubtree(src->type, (ndt_t *)x->type);
1764
+ if (type == NULL) {
1765
+ return NULL;
1766
+ }
1767
+
1768
+ view = pyxnd_alloc(Py_TYPE(src));
1769
+ if (view == NULL) {
1770
+ Py_DECREF(type);
1771
+ return NULL;
1772
+ }
1773
+
1774
+ Py_INCREF(src->mblock);
1775
+ view->mblock = src->mblock;
1776
+ view->type = type;
1777
+ view->xnd = *x;
1778
+
1779
+ return (PyObject *)view;
1780
+ }
1781
+
1782
+ static Py_ssize_t
1783
+ pyxnd_len(const xnd_t *x)
1784
+ {
1785
+ NDT_STATIC_CONTEXT(ctx);
1786
+ const ndt_t *t = x->type;
1787
+
1788
+ assert(ndt_is_concrete(t));
1789
+
1790
+ if (t->ndim > 0 && ndt_is_optional(t)) {
1791
+ PyErr_SetString(PyExc_NotImplementedError,
1792
+ "optional dimensions are not supported");
1793
+ return -1;
1794
+ }
1795
+
1796
+ if (xnd_is_na(x)) {
1797
+ return 0;
1798
+ }
1799
+
1800
+ switch (t->tag) {
1801
+ case FixedDim: {
1802
+ return safe_downcast(t->FixedDim.shape);
1803
+ }
1804
+
1805
+ case VarDim: {
1806
+ int64_t start, step, shape;
1807
+
1808
+ shape = ndt_var_indices(&start, &step, t, x->index, &ctx);
1809
+ if (shape < 0) {
1810
+ return seterr_int(&ctx);
1811
+ }
1812
+
1813
+ return safe_downcast(shape);
1814
+ }
1815
+
1816
+ case Tuple: {
1817
+ return safe_downcast(t->Tuple.shape);
1818
+ }
1819
+
1820
+ case Record: {
1821
+ return safe_downcast(t->Record.shape);
1822
+ }
1823
+
1824
+ case Ref: {
1825
+ const xnd_t next = xnd_ref_next(x, &ctx);
1826
+ if (next.ptr == NULL) {
1827
+ return seterr_int(&ctx);
1828
+ }
1829
+
1830
+ return pyxnd_len(&next);
1831
+ }
1832
+
1833
+ case Constr: {
1834
+ const xnd_t next = xnd_constr_next(x, &ctx);
1835
+ if (next.ptr == NULL) {
1836
+ return seterr_int(&ctx);
1837
+ }
1838
+
1839
+ return pyxnd_len(&next);
1840
+ }
1841
+
1842
+ case Nominal: {
1843
+ const xnd_t next = xnd_nominal_next(x, &ctx);
1844
+ if (next.ptr == NULL) {
1845
+ return seterr_int(&ctx);
1846
+ }
1847
+
1848
+ return pyxnd_len(&next);
1849
+ }
1850
+
1851
+ default:
1852
+ PyErr_SetString(PyExc_TypeError, "type has no len()");
1853
+ return -1;
1854
+ }
1855
+ }
1856
+
1857
+ static Py_ssize_t
1858
+ pyxnd_length(XndObject *self)
1859
+ {
1860
+ return pyxnd_len(XND(self));
1861
+ }
1862
+
1863
+ #define KEY_INDEX 1
1864
+ #define KEY_FIELD 2
1865
+ #define KEY_SLICE 4
1866
+ #define KEY_ERROR 128
1867
+
1868
+ static uint8_t
1869
+ convert_single(xnd_index_t *key, PyObject *obj)
1870
+ {
1871
+ if (PyIndex_Check(obj)) {
1872
+ int64_t i = PyNumber_AsSsize_t(obj, PyExc_IndexError);
1873
+
1874
+ if (i == -1 && PyErr_Occurred()) {
1875
+ return KEY_ERROR;
1876
+ }
1877
+
1878
+ key->tag = Index;
1879
+ key->Index = i;
1880
+ return KEY_INDEX;
1881
+ }
1882
+ else if (PyUnicode_Check(obj)) {
1883
+ const char *s = PyUnicode_AsUTF8(obj);
1884
+
1885
+ if (s == NULL) {
1886
+ return KEY_ERROR;
1887
+ }
1888
+
1889
+ key->tag = FieldName;
1890
+ key->FieldName = s;
1891
+ return KEY_FIELD;
1892
+ }
1893
+ else if (PySlice_Check(obj)) {
1894
+ Py_ssize_t start;
1895
+ Py_ssize_t stop;
1896
+ Py_ssize_t step;
1897
+
1898
+ if (pyslice_unpack(obj, &start, &stop, &step) < 0) {
1899
+ return KEY_ERROR;
1900
+ }
1901
+
1902
+ key->tag = Slice;
1903
+ key->Slice.start = start;
1904
+ key->Slice.stop = stop;
1905
+ key->Slice.step = step;
1906
+ return KEY_SLICE;
1907
+ }
1908
+ else {
1909
+ PyErr_SetString(PyExc_TypeError, "invalid subscript key");
1910
+ return KEY_ERROR;
1911
+ }
1912
+ }
1913
+
1914
+ uint8_t
1915
+ convert_key(xnd_index_t *indices, int *len, PyObject *key)
1916
+ {
1917
+ uint8_t flags = 0;
1918
+
1919
+ if (PyTuple_Check(key)) {
1920
+ Py_ssize_t size = PyTuple_GET_SIZE(key);
1921
+
1922
+ if (size > NDT_MAX_DIM) {
1923
+ PyErr_SetString(PyExc_IndexError, "too many indices");
1924
+ return KEY_ERROR;
1925
+ }
1926
+
1927
+ for (Py_ssize_t i = 0; i < size; i++) {
1928
+ PyObject *x = PyTuple_GET_ITEM(key, i);
1929
+ flags |= convert_single(indices+i, x);
1930
+ if (flags & KEY_ERROR) {
1931
+ return KEY_ERROR;
1932
+ }
1933
+ }
1934
+
1935
+ *len = size;
1936
+ return flags;
1937
+ }
1938
+
1939
+ *len = 1;
1940
+ return convert_single(indices, key);
1941
+ }
1942
+
1943
+ static PyObject *
1944
+ pyxnd_subscript(XndObject *self, PyObject *key)
1945
+ {
1946
+ NDT_STATIC_CONTEXT(ctx);
1947
+ xnd_index_t indices[NDT_MAX_DIM];
1948
+ xnd_t x;
1949
+ int len;
1950
+ uint8_t flags;
1951
+
1952
+ flags = convert_key(indices, &len, key);
1953
+ if (flags & KEY_ERROR) {
1954
+ return NULL;
1955
+ }
1956
+
1957
+ x = xnd_subscript(&self->xnd, indices, len, &ctx);
1958
+ if (x.ptr == NULL) {
1959
+ return seterr(&ctx);
1960
+ }
1961
+
1962
+ return pyxnd_view_move_type(self, &x);
1963
+ }
1964
+
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
+ static PyObject *
1976
+ pyxnd_split(PyObject *self, PyObject *args, PyObject *kwds)
1977
+ {
1978
+ static char *kwlist[] = {"n", "max_outer", NULL};
1979
+ 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)) {
1988
+ return NULL;
1989
+ }
1990
+
1991
+ n = PyLong_AsLongLong(nparts);
1992
+ if (n == -1 && PyErr_Occurred()) {
1993
+ return NULL;
1994
+ }
1995
+
1996
+ if (max != Py_None) {
1997
+ long l = PyLong_AsLong(max);
1998
+ if (l == -1 && PyErr_Occurred()) {
1999
+ return NULL;
2000
+ }
2001
+ if (l < 0 || l > NDT_MAX_DIM) {
2002
+ PyErr_SetString(PyExc_ValueError,
2003
+ "max_outer must be in [0, NDT_MAX_DIM]");
2004
+ return NULL;
2005
+ }
2006
+ max_outer = (int)l;
2007
+ }
2008
+
2009
+ slices = xnd_split(XND(self), &n, max_outer, &ctx);
2010
+ if (slices == NULL) {
2011
+ return seterr(&ctx);
2012
+ }
2013
+
2014
+ res = PyList_New(n);
2015
+ if (res == NULL) {
2016
+ free_slices(slices, 0, n);
2017
+ return NULL;
2018
+ }
2019
+
2020
+ for (int64_t i = 0; i < n; i++) {
2021
+ PyObject *x = pyxnd_view_move_type((XndObject *)self, &slices[i]);
2022
+ if (x == NULL) {
2023
+ free_slices(slices, i+1, n);
2024
+ Py_DECREF(res);
2025
+ return NULL;
2026
+ }
2027
+ PyList_SET_ITEM(res, i, x);
2028
+ }
2029
+
2030
+ ndt_free(slices);
2031
+
2032
+ return res;
2033
+ }
2034
+
2035
+ static int
2036
+ pyxnd_assign(XndObject *self, PyObject *key, PyObject *value)
2037
+ {
2038
+ NDT_STATIC_CONTEXT(ctx);
2039
+ xnd_index_t indices[NDT_MAX_DIM];
2040
+ xnd_t x;
2041
+ int free_type = 0;
2042
+ int ret, len;
2043
+ uint8_t flags;
2044
+
2045
+ if (value == NULL) {
2046
+ PyErr_SetString(PyExc_TypeError, "cannot delete memory blocks");
2047
+ return -1;
2048
+ }
2049
+
2050
+ if (is_readonly(self)) {
2051
+ PyErr_SetString(PyExc_TypeError, "memory block is read-only");
2052
+ return -1;
2053
+ }
2054
+
2055
+ flags = convert_key(indices, &len, key);
2056
+ if (flags & KEY_ERROR) {
2057
+ return -1;
2058
+ }
2059
+
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
+
2074
+ if (x.ptr == NULL) {
2075
+ return seterr_int(&ctx);
2076
+ }
2077
+
2078
+ if (Xnd_Check(value)) {
2079
+ ret = xnd_copy(&x, XND(value), self->mblock->xnd->flags, &ctx);
2080
+ if (ret < 0) {
2081
+ (void)seterr_int(&ctx);
2082
+ }
2083
+ }
2084
+ else {
2085
+ ret = mblock_init(&x, value);
2086
+ }
2087
+
2088
+ if (free_type) {
2089
+ ndt_del((ndt_t *)x.type);
2090
+ }
2091
+
2092
+ return ret;
2093
+ }
2094
+
2095
+ static PyObject *
2096
+ pyxnd_item(XndObject *self, Py_ssize_t index)
2097
+ {
2098
+ PyObject *res;
2099
+ PyObject *key;
2100
+
2101
+ key = PyLong_FromSsize_t(index);
2102
+ if (key == NULL) {
2103
+ return NULL;
2104
+ }
2105
+
2106
+ res = pyxnd_subscript(self, key);
2107
+ Py_DECREF(key);
2108
+ return res;
2109
+ }
2110
+
2111
+ static PyObject *
2112
+ pyxnd_short_value(PyObject *self, PyObject *args, PyObject *kwds)
2113
+ {
2114
+ static char *kwlist[] = {"maxshape", NULL};
2115
+ PyObject *maxshape = Py_None;
2116
+
2117
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "|O", kwlist, &maxshape)) {
2118
+ return NULL;
2119
+ }
2120
+
2121
+ if (maxshape == Py_None) {
2122
+ return _pyxnd_value(XND(self), INT64_MAX);
2123
+ }
2124
+ else {
2125
+ Py_ssize_t max = PyLong_AsSsize_t(maxshape);
2126
+ if (max == -1 && PyErr_Occurred()) {
2127
+ return NULL;
2128
+ }
2129
+ if (max < 0) {
2130
+ PyErr_SetString(PyExc_ValueError, "maxshape must be positive");
2131
+ return NULL;
2132
+ }
2133
+
2134
+ return _pyxnd_value(XND(self), max);
2135
+ }
2136
+ }
2137
+
2138
+ static PyObject *
2139
+ pyxnd_strict_equal(PyObject *self, PyObject *other)
2140
+ {
2141
+ NDT_STATIC_CONTEXT(ctx);
2142
+ PyObject *res;
2143
+ int r;
2144
+
2145
+ if (!Xnd_Check(other)) {
2146
+ PyErr_SetString(PyExc_TypeError,
2147
+ "strict_equal requires an xnd argument");
2148
+ return NULL;
2149
+ }
2150
+
2151
+ r = xnd_strict_equal(XND(self), XND(other), &ctx);
2152
+ if (r < 0) {
2153
+ return seterr(&ctx);
2154
+ }
2155
+
2156
+ res = r ? Py_True : Py_False;
2157
+ Py_INCREF(res);
2158
+ return res;
2159
+ }
2160
+
2161
+ static PyObject *
2162
+ pyxnd_type(PyObject *self, PyObject *args UNUSED)
2163
+ {
2164
+ Py_INCREF(TYPE_OWNER(self));
2165
+ return TYPE_OWNER(self);
2166
+ }
2167
+
2168
+ static PyObject *
2169
+ pyxnd_ndim(PyObject *self, PyObject *args UNUSED)
2170
+ {
2171
+ int ndim = XND_TYPE(self)->ndim;
2172
+ return PyLong_FromLong(ndim);
2173
+ }
2174
+
2175
+ static PyObject *
2176
+ pyxnd_value(PyObject *self, PyObject *args UNUSED)
2177
+ {
2178
+ return _pyxnd_value(XND(self), INT64_MAX);
2179
+ }
2180
+
2181
+ static PyObject *
2182
+ pyxnd_align(PyObject *self, PyObject *args UNUSED)
2183
+ {
2184
+ uint16_t align = XND_TYPE(self)->align;
2185
+ return PyLong_FromUnsignedLong(align);
2186
+ }
2187
+
2188
+
2189
+ static PyGetSetDef pyxnd_getsets [] =
2190
+ {
2191
+ { "type", (getter)pyxnd_type, NULL, doc_type, NULL},
2192
+ { "value", (getter)pyxnd_value, NULL, doc_value, NULL},
2193
+ { "align", (getter)pyxnd_align, NULL, doc_align, NULL},
2194
+ { "ndim", (getter)pyxnd_ndim, NULL, doc_ndim, NULL},
2195
+ {NULL}
2196
+ };
2197
+
2198
+ static PyMappingMethods pyxnd_as_mapping = {
2199
+ (lenfunc)pyxnd_length, /* mp_length */
2200
+ (binaryfunc)pyxnd_subscript, /* mp_subscript */
2201
+ (objobjargproc)pyxnd_assign, /* mp_ass_subscript */
2202
+ };
2203
+
2204
+ static PySequenceMethods pyxnd_as_sequence = {
2205
+ (lenfunc)pyxnd_length, /* sq_length */
2206
+ 0, /* sq_concat */
2207
+ 0, /* sq_repeat */
2208
+ (ssizeargfunc)pyxnd_item, /* sq_item */
2209
+ };
2210
+
2211
+
2212
+ static PyMethodDef pyxnd_methods [] =
2213
+ {
2214
+ /* Methods */
2215
+ { "short_value", (PyCFunction)pyxnd_short_value, METH_VARARGS|METH_KEYWORDS, doc_short_value },
2216
+ { "strict_equal", (PyCFunction)pyxnd_strict_equal, METH_O, NULL },
2217
+ { "split", (PyCFunction)pyxnd_split, METH_VARARGS|METH_KEYWORDS, NULL },
2218
+
2219
+ /* Class methods */
2220
+ { "empty", (PyCFunction)pyxnd_empty, METH_O|METH_CLASS, doc_empty },
2221
+ { "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 },
2223
+
2224
+ { NULL, NULL, 1 }
2225
+ };
2226
+
2227
+
2228
+ /****************************************************************************/
2229
+ /* Buffer exports */
2230
+ /****************************************************************************/
2231
+
2232
+ static PyTypeObject BufferProxy_Type;
2233
+
2234
+ typedef struct {
2235
+ PyObject_HEAD
2236
+ XndObject *xnd;
2237
+ Py_buffer view;
2238
+ Py_ssize_t ob_array[1];
2239
+ } BufferProxyObject;
2240
+
2241
+ static BufferProxyObject *
2242
+ buffer_alloc(XndObject *xnd)
2243
+ {
2244
+ const ndt_t *t = XND_TYPE(xnd);
2245
+ BufferProxyObject *self;
2246
+
2247
+ self = (BufferProxyObject *)
2248
+ PyObject_NewVar(BufferProxyObject, &BufferProxy_Type, 2 * t->ndim);
2249
+ if (self == NULL) {
2250
+ return NULL;
2251
+ }
2252
+
2253
+ Py_INCREF(xnd);
2254
+ self->xnd = xnd;
2255
+
2256
+ self->view.buf = NULL;
2257
+ self->view.obj = NULL;
2258
+ self->view.len = 0;
2259
+ self->view.itemsize = 0;
2260
+ self->view.readonly = 0;
2261
+
2262
+ self->view.ndim = t->ndim;
2263
+ self->view.format = NULL;
2264
+ self->view.shape = self->ob_array;
2265
+ self->view.strides = self->ob_array + t->ndim;
2266
+ self->view.suboffsets = NULL;
2267
+ self->view.internal = NULL;
2268
+
2269
+ return self;
2270
+ }
2271
+
2272
+ static void
2273
+ buffer_dealloc(BufferProxyObject *self)
2274
+ {
2275
+ ndt_free(self->view.format);
2276
+ self->view.format = NULL;
2277
+ Py_CLEAR(self->xnd);
2278
+ PyObject_Del(self);
2279
+ }
2280
+
2281
+ static int
2282
+ fill_buffer(Py_buffer *view, const xnd_t *x, ndt_context_t *ctx)
2283
+ {
2284
+ const ndt_t *t = x->type;
2285
+ char *fmt;
2286
+ Py_ssize_t len;
2287
+ int i;
2288
+
2289
+ assert(t->ndim <= NDT_MAX_DIM);
2290
+ assert(ndt_is_concrete(t));
2291
+
2292
+ fmt = ndt_to_bpformat(ndt_dtype(t), ctx);
2293
+ if (fmt == NULL) {
2294
+ return -1;
2295
+ }
2296
+
2297
+ view->ndim = t->ndim;
2298
+ view->format = fmt;
2299
+ view->suboffsets = NULL;
2300
+
2301
+ if (!ndt_is_ndarray(t)) {
2302
+ ndt_err_format(ctx, NDT_ValueError,
2303
+ "buffer protocol only supports ndarrays");
2304
+ return -1;
2305
+ }
2306
+
2307
+ if (t->ndim == 0) {
2308
+ view->len = (Py_ssize_t)t->datasize;
2309
+ view->itemsize = (Py_ssize_t)t->datasize;
2310
+ view->shape = NULL;
2311
+ view->strides = NULL;
2312
+ view->buf = x->ptr + x->index * t->datasize;
2313
+ return 0;
2314
+ }
2315
+
2316
+ view->itemsize = (Py_ssize_t)t->Concrete.FixedDim.itemsize;
2317
+ view->buf = x->ptr + x->index * view->itemsize;
2318
+
2319
+ len = 1;
2320
+ for (i=0; t->ndim > 0; i++, t=t->FixedDim.type) {
2321
+ view->shape[i] = (Py_ssize_t)t->FixedDim.shape;
2322
+ view->strides[i] = (Py_ssize_t)(t->Concrete.FixedDim.step * view->itemsize);
2323
+ len *= view->shape[i];
2324
+ }
2325
+ len *= view->itemsize;
2326
+
2327
+ view->len = len;
2328
+
2329
+ return 0;
2330
+ }
2331
+
2332
+ static int
2333
+ pyxnd_getbuf(XndObject *self, Py_buffer *view, int flags)
2334
+ {
2335
+ NDT_STATIC_CONTEXT(ctx);
2336
+ BufferProxyObject *proxy;
2337
+
2338
+ if (flags != PyBUF_FULL && flags != PyBUF_FULL_RO) {
2339
+ PyErr_SetString(PyExc_ValueError,
2340
+ "only PyBUF_FULL and PyBUF_FULL_RO requests are supported");
2341
+ return -1;
2342
+ }
2343
+
2344
+ proxy = buffer_alloc(self);
2345
+ if (proxy == NULL) {
2346
+ return -1;
2347
+ }
2348
+
2349
+ if (fill_buffer(&proxy->view, XND(self), &ctx) < 0) {
2350
+ Py_DECREF(proxy);
2351
+ return seterr_int(&ctx);
2352
+ }
2353
+
2354
+ *view = proxy->view;
2355
+ view->obj = (PyObject *)proxy;
2356
+
2357
+ return 0;
2358
+ }
2359
+
2360
+ static void
2361
+ pyxnd_releasebuf(XndObject *self UNUSED, Py_buffer *view UNUSED)
2362
+ {
2363
+ return;
2364
+ /* PyBuffer_Release() decrements view->obj after this function returns. */
2365
+ }
2366
+
2367
+
2368
+ static PyTypeObject BufferProxy_Type =
2369
+ {
2370
+ PyVarObject_HEAD_INIT(NULL, 0)
2371
+ .tp_name = "xnd.BufferProxy",
2372
+ .tp_basicsize = offsetof(BufferProxyObject, ob_array),
2373
+ .tp_itemsize = sizeof(Py_ssize_t),
2374
+ .tp_dealloc = (destructor) buffer_dealloc,
2375
+ .tp_hash = PyObject_HashNotImplemented,
2376
+ .tp_getattro = (getattrofunc) PyObject_GenericGetAttr,
2377
+ .tp_flags = Py_TPFLAGS_DEFAULT,
2378
+ };
2379
+
2380
+ static PyBufferProcs pyxnd_as_buffer = {
2381
+ (getbufferproc)pyxnd_getbuf, /* bf_getbuffer */
2382
+ (releasebufferproc)pyxnd_releasebuf, /* bf_releasebuffer */
2383
+ };
2384
+
2385
+
2386
+ /* Special methods */
2387
+ static PyObject *
2388
+ convert_cmp(PyObject *v, PyObject *w, int op)
2389
+ {
2390
+ PyObject *vcmp;
2391
+ PyObject *res;
2392
+
2393
+ assert(!Xnd_Check(w));
2394
+
2395
+ vcmp = _pyxnd_value(XND(v), INT64_MAX);
2396
+ if (vcmp == NULL) {
2397
+ return NULL;
2398
+ }
2399
+
2400
+ res = PyObject_RichCompare(vcmp, w, op);
2401
+ Py_DECREF(vcmp);
2402
+ return res;
2403
+ }
2404
+
2405
+ static PyObject *
2406
+ pyxnd_richcompare(PyObject *v, PyObject *w, int op)
2407
+ {
2408
+ NDT_STATIC_CONTEXT(ctx);
2409
+ PyObject *res = Py_NotImplemented;
2410
+
2411
+ assert(Xnd_Check(v));
2412
+
2413
+ if (!Xnd_Check(w)) {
2414
+ return convert_cmp(v, w, op);
2415
+ }
2416
+
2417
+ if (op == Py_EQ || op == Py_NE) {
2418
+ int r = xnd_equal(XND(v), XND(w), &ctx);
2419
+ if (r < 0) {
2420
+ return seterr(&ctx);
2421
+ }
2422
+
2423
+ res = ((op==Py_EQ) == !!r) ? Py_True : Py_False;
2424
+ }
2425
+
2426
+ Py_INCREF(res);
2427
+ return res;
2428
+ }
2429
+
2430
+
2431
+ /****************************************************************************/
2432
+ /* Xnd type */
2433
+ /****************************************************************************/
2434
+
2435
+ static PyTypeObject Xnd_Type =
2436
+ {
2437
+ PyVarObject_HEAD_INIT(NULL, 0)
2438
+ .tp_name = "xnd.Xnd",
2439
+ .tp_basicsize = sizeof(XndObject),
2440
+ .tp_dealloc = (destructor) pyxnd_dealloc,
2441
+ .tp_as_sequence = &pyxnd_as_sequence,
2442
+ .tp_as_mapping = &pyxnd_as_mapping,
2443
+ .tp_hash = PyObject_HashNotImplemented,
2444
+ .tp_getattro = (getattrofunc) PyObject_GenericGetAttr,
2445
+ .tp_as_buffer = &pyxnd_as_buffer,
2446
+ .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC,
2447
+ .tp_richcompare = pyxnd_richcompare,
2448
+ .tp_traverse = (traverseproc)pyxnd_traverse,
2449
+ .tp_methods = pyxnd_methods,
2450
+ .tp_getset = pyxnd_getsets,
2451
+ .tp_alloc = PyType_GenericAlloc,
2452
+ .tp_new = pyxnd_new,
2453
+ .tp_free = PyObject_GC_Del
2454
+ };
2455
+
2456
+
2457
+ /****************************************************************************/
2458
+ /* C-API */
2459
+ /****************************************************************************/
2460
+
2461
+ static void **xnd_api[XND_MAX_API];
2462
+
2463
+ static int
2464
+ Xnd_CheckExact(const PyObject *v)
2465
+ {
2466
+ return Py_TYPE(v) == &Xnd_Type;
2467
+ }
2468
+
2469
+ static int
2470
+ Xnd_Check(const PyObject *v)
2471
+ {
2472
+ return PyObject_TypeCheck(v, &Xnd_Type);
2473
+ }
2474
+
2475
+ static const xnd_t *
2476
+ CONST_XND(const PyObject *v)
2477
+ {
2478
+ assert(Xnd_Check(v));
2479
+ return &((XndObject *)v)->xnd;
2480
+ }
2481
+
2482
+ static PyObject *
2483
+ Xnd_EmptyFromType(PyTypeObject *tp, ndt_t *t)
2484
+ {
2485
+ MemoryBlockObject *mblock;
2486
+ PyObject *type;
2487
+
2488
+ type = Ndt_FromType(t);
2489
+ if (type == NULL) {
2490
+ return NULL;
2491
+ }
2492
+
2493
+ mblock = mblock_empty(type);
2494
+ Py_DECREF(type);
2495
+ if (mblock == NULL) {
2496
+ return NULL;
2497
+ }
2498
+
2499
+ return pyxnd_from_mblock(tp, mblock);
2500
+ }
2501
+
2502
+ static PyObject *
2503
+ Xnd_ViewMoveNdt(const PyObject *v, ndt_t *t)
2504
+ {
2505
+ XndObject *src = (XndObject *)v;
2506
+ XndObject *view;
2507
+ PyObject *type;
2508
+
2509
+ if (!Xnd_Check(v)) {
2510
+ PyErr_SetString(PyExc_TypeError, "expected xnd object");
2511
+ ndt_del(t);
2512
+ return NULL;
2513
+ }
2514
+
2515
+ type = Ndt_MoveSubtree(src->type, t);
2516
+ if (type == NULL) {
2517
+ return NULL;
2518
+ }
2519
+
2520
+ view = pyxnd_alloc(Py_TYPE(src));
2521
+ if (view == NULL) {
2522
+ Py_DECREF(type);
2523
+ return NULL;
2524
+ }
2525
+
2526
+ Py_INCREF(src->mblock);
2527
+ view->mblock = src->mblock;
2528
+ view->type = type;
2529
+ view->xnd = src->xnd;
2530
+ view->xnd.type = t;
2531
+
2532
+ return (PyObject *)view;
2533
+ }
2534
+
2535
+ static PyObject *
2536
+ Xnd_FromXnd(PyTypeObject *tp, xnd_t *x)
2537
+ {
2538
+ MemoryBlockObject *mblock;
2539
+
2540
+ mblock = mblock_from_xnd(x);
2541
+ if (mblock == NULL) {
2542
+ return NULL;
2543
+ }
2544
+
2545
+ return pyxnd_from_mblock(tp, mblock);
2546
+ }
2547
+
2548
+ static PyObject *
2549
+ Xnd_Subscript(const PyObject *self, const PyObject *key)
2550
+ {
2551
+ if (!Xnd_Check(self)) {
2552
+ PyErr_SetString(PyExc_TypeError,
2553
+ "xnd subscript function called on non-xnd object");
2554
+ return NULL;
2555
+ }
2556
+
2557
+ return pyxnd_subscript((XndObject *)self, (PyObject *)key);
2558
+ }
2559
+
2560
+ /*
2561
+ * The 'xnd' argument provides a link to the owner of the memory and type
2562
+ * resources. 'x' is a view (usually a subtree or a slice) that is based
2563
+ * on 'xnd'. x->type belongs to 'x' and ownership is transferred to the
2564
+ * result.
2565
+ *
2566
+ * In case of an error, x->type is deallocated.
2567
+ */
2568
+ static PyObject *
2569
+ Xnd_FromXndMoveType(const PyObject *xnd, xnd_t *x)
2570
+ {
2571
+ if (!Xnd_Check(xnd)) {
2572
+ PyErr_SetString(PyExc_TypeError,
2573
+ "Xnd_FromXndMoveType() called on non-xnd object");
2574
+ ndt_del((ndt_t *)x->type);
2575
+ return NULL;
2576
+ }
2577
+
2578
+ return pyxnd_view_move_type((const XndObject *)xnd, x);
2579
+ }
2580
+
2581
+ /* Get the type from __init__.py with the pretty representation. */
2582
+ static PyTypeObject *
2583
+ Xnd_GetType(void)
2584
+ {
2585
+ static PyTypeObject *type = NULL;
2586
+
2587
+ if (type == NULL) {
2588
+ PyObject *obj = PyImport_ImportModule("xnd");
2589
+ if (obj == NULL) {
2590
+ return NULL;
2591
+ }
2592
+
2593
+ type = (PyTypeObject *)PyObject_GetAttrString(obj, "xnd");
2594
+ Py_CLEAR(obj);
2595
+ if (type == NULL) {
2596
+ return NULL;
2597
+ }
2598
+ }
2599
+
2600
+ Py_INCREF(type);
2601
+ return type;
2602
+ }
2603
+
2604
+ /*
2605
+ * This function handles two common view cases:
2606
+ *
2607
+ * a) A pristine view that owns everything, including new memory.
2608
+ * b) A view that owns its type after xnd_subscript().
2609
+ */
2610
+ static PyObject *
2611
+ Xnd_FromXndView(xnd_view_t *x)
2612
+ {
2613
+ if (x->obj == NULL && (x->flags&XND_OWN_ALL)==XND_OWN_ALL) {
2614
+ PyTypeObject *type = Xnd_GetType();
2615
+ if (type == NULL) {
2616
+ xnd_view_clear(x);
2617
+ return NULL;
2618
+ }
2619
+
2620
+ return Xnd_FromXnd(type, &x->view);
2621
+ }
2622
+ else if (x->obj != NULL && (x->flags&XND_OWN_TYPE)) {
2623
+ return Xnd_FromXndMoveType(x->obj, &x->view);
2624
+ }
2625
+ else {
2626
+ PyErr_SetString(PyExc_TypeError,
2627
+ "Xnd_FromXndView: unsupported combination of flags and "
2628
+ "resource owner");
2629
+ xnd_view_clear(x);
2630
+ return NULL;
2631
+ }
2632
+ }
2633
+
2634
+ static PyObject *
2635
+ init_api(void)
2636
+ {
2637
+ xnd_api[Xnd_CheckExact_INDEX] = (void *)Xnd_CheckExact;
2638
+ xnd_api[Xnd_Check_INDEX] = (void *)Xnd_Check;
2639
+ xnd_api[CONST_XND_INDEX] = (void *)CONST_XND;
2640
+ xnd_api[Xnd_EmptyFromType_INDEX] = (void *)Xnd_EmptyFromType;
2641
+ xnd_api[Xnd_ViewMoveNdt_INDEX] = (void *)Xnd_ViewMoveNdt;
2642
+ xnd_api[Xnd_FromXnd_INDEX] = (void *)Xnd_FromXnd;
2643
+ xnd_api[Xnd_Subscript_INDEX] = (void *)Xnd_Subscript;
2644
+ xnd_api[Xnd_FromXndMoveType_INDEX] = (void *)Xnd_FromXndMoveType;
2645
+ xnd_api[Xnd_FromXndView_INDEX] = (void *)Xnd_FromXndView;
2646
+ xnd_api[Xnd_GetType_INDEX] = (void *)Xnd_GetType;
2647
+
2648
+ return PyCapsule_New(xnd_api, "xnd._xnd._API", NULL);
2649
+ }
2650
+
2651
+
2652
+ /****************************************************************************/
2653
+ /* Test functions (will be moved into a separate module) */
2654
+ /****************************************************************************/
2655
+
2656
+ /* Test the xnd_view_t API. */
2657
+ static PyObject *
2658
+ _test_view_subscript(PyObject *module UNUSED, PyObject *args, PyObject *kwds)
2659
+ {
2660
+ static char *kwlist[] = {"x", "key", NULL};
2661
+ NDT_STATIC_CONTEXT(ctx);
2662
+ PyObject *x = NULL;
2663
+ PyObject *key = NULL;
2664
+ xnd_index_t indices[NDT_MAX_DIM];
2665
+ xnd_view_t v, u;
2666
+ int len;
2667
+ uint8_t flags;
2668
+
2669
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "OO", kwlist, &x, &key)) {
2670
+ return NULL;
2671
+ }
2672
+
2673
+ if (!Xnd_Check(x)) {
2674
+ PyErr_SetString(PyExc_TypeError,
2675
+ "_test_view expects an xnd argument");
2676
+ return NULL;
2677
+ }
2678
+
2679
+ flags = convert_key(indices, &len, key);
2680
+ if (flags & KEY_ERROR) {
2681
+ return NULL;
2682
+ }
2683
+
2684
+ /* Fill in the view (this sets the resource owner). */
2685
+ v = xnd_view_from_xnd(x, XND(x));
2686
+
2687
+ /* Subscript the view (this updates all resource flags). */
2688
+ u = xnd_view_subscript(&v, indices, len, &ctx);
2689
+ if (ndt_err_occurred(&ctx)) {
2690
+ return seterr(&ctx);
2691
+
2692
+ }
2693
+
2694
+ return Xnd_FromXndView(&u);
2695
+ }
2696
+
2697
+ static PyObject *
2698
+ _test_view_new(PyObject *module UNUSED, PyObject *args UNUSED)
2699
+ {
2700
+ NDT_STATIC_CONTEXT(ctx);
2701
+ xnd_view_t x = xnd_view_error;
2702
+ double *d;
2703
+ ndt_t *t;
2704
+ char *ptr;
2705
+
2706
+ t = ndt_from_string("3 * float64", &ctx);
2707
+ if (t == NULL) {
2708
+ return seterr(&ctx);
2709
+ }
2710
+
2711
+ ptr = ndt_aligned_calloc(8, 3 * sizeof(double));
2712
+ if (ptr == NULL) {
2713
+ ndt_del(t);
2714
+ (void)ndt_memory_error(&ctx);
2715
+ return seterr(&ctx);
2716
+ }
2717
+
2718
+ d = (double *)ptr;
2719
+ d[0] = 1.1;
2720
+ d[1] = 2.2;
2721
+ d[2] = 3.3;
2722
+
2723
+ x.flags = XND_OWN_ALL;
2724
+ x.obj = NULL;
2725
+ x.view.index = 0;
2726
+ x.view.type = t;
2727
+ x.view.ptr = ptr;
2728
+
2729
+ return Xnd_FromXndView(&x);
2730
+ }
2731
+
2732
+
2733
+ /****************************************************************************/
2734
+ /* Module */
2735
+ /****************************************************************************/
2736
+
2737
+ static PyMethodDef _xnd_methods [] =
2738
+ {
2739
+ { "_test_view_subscript", (PyCFunction)_test_view_subscript, METH_VARARGS|METH_KEYWORDS, NULL},
2740
+ { "_test_view_new", (PyCFunction)_test_view_new, METH_NOARGS, NULL},
2741
+ { NULL, NULL, 1, NULL }
2742
+ };
2743
+
2744
+ static struct PyModuleDef xnd_module = {
2745
+ PyModuleDef_HEAD_INIT, /* m_base */
2746
+ "_xnd", /* m_name */
2747
+ doc_module, /* m_doc */
2748
+ -1, /* m_size */
2749
+ _xnd_methods, /* m_methods */
2750
+ NULL, /* m_slots */
2751
+ NULL, /* m_traverse */
2752
+ NULL, /* m_clear */
2753
+ NULL /* m_free */
2754
+ };
2755
+
2756
+
2757
+ PyMODINIT_FUNC
2758
+ PyInit__xnd(void)
2759
+ {
2760
+ NDT_STATIC_CONTEXT(ctx);
2761
+ PyObject *m = NULL;
2762
+ static PyObject *capsule = NULL;
2763
+ static int initialized = 0;
2764
+
2765
+ if (!initialized) {
2766
+ if (xnd_init_float(&ctx) < 0) {
2767
+ return seterr(&ctx);
2768
+ }
2769
+ if (import_ndtypes() < 0) {
2770
+ return NULL;
2771
+ }
2772
+ capsule = init_api();
2773
+ if (capsule == NULL) {
2774
+ return NULL;
2775
+ }
2776
+ initialized = 1;
2777
+ }
2778
+
2779
+ if (PyType_Ready(&XndEllipsis_Type) < 0) {
2780
+ return NULL;
2781
+ }
2782
+
2783
+ if (PyType_Ready(&MemoryBlock_Type) < 0) {
2784
+ return NULL;
2785
+ }
2786
+
2787
+ if (PyType_Ready(&BufferProxy_Type) < 0) {
2788
+ return NULL;
2789
+ }
2790
+
2791
+ Xnd_Type.tp_base = &PyBaseObject_Type;
2792
+ if (PyType_Ready(&Xnd_Type) < 0) {
2793
+ return NULL;
2794
+ }
2795
+
2796
+ m = PyModule_Create(&xnd_module);
2797
+ if (m == NULL) {
2798
+ goto error;
2799
+ }
2800
+
2801
+ if (PyModule_AddObject(m, "XndEllipsis", xnd_ellipsis()) < 0) {
2802
+ goto error;
2803
+ }
2804
+
2805
+ Py_INCREF(&Xnd_Type);
2806
+ if (PyModule_AddObject(m, "Xnd", (PyObject *)&Xnd_Type) < 0) {
2807
+ goto error;
2808
+ }
2809
+
2810
+ Py_INCREF(capsule);
2811
+ if (PyModule_AddObject(m, "_API", capsule) < 0) {
2812
+ goto error;
2813
+ }
2814
+
2815
+ return m;
2816
+
2817
+ error:
2818
+ Py_CLEAR(m);
2819
+ return NULL;
2820
+ }
2821
+
2822
+