xnd 0.2.0dev3
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.
- checksums.yaml +7 -0
- data/CONTRIBUTING.md +42 -0
- data/Gemfile +3 -0
- data/History.md +0 -0
- data/README.md +7 -0
- data/Rakefile +135 -0
- data/ext/ruby_xnd/extconf.rb +70 -0
- data/ext/ruby_xnd/float_pack_unpack.c +277 -0
- data/ext/ruby_xnd/float_pack_unpack.h +39 -0
- data/ext/ruby_xnd/gc_guard.c +36 -0
- data/ext/ruby_xnd/gc_guard.h +12 -0
- data/ext/ruby_xnd/include/xnd.h +449 -0
- data/ext/ruby_xnd/lib/libxnd.a +0 -0
- data/ext/ruby_xnd/lib/libxnd.so +1 -0
- data/ext/ruby_xnd/lib/libxnd.so.0 +1 -0
- data/ext/ruby_xnd/lib/libxnd.so.0.2.0dev3 +0 -0
- data/ext/ruby_xnd/memory_block_object.c +32 -0
- data/ext/ruby_xnd/memory_block_object.h +33 -0
- data/ext/ruby_xnd/ruby_xnd.c +1953 -0
- data/ext/ruby_xnd/ruby_xnd.h +61 -0
- data/ext/ruby_xnd/ruby_xnd_internal.h +85 -0
- data/ext/ruby_xnd/util.h +170 -0
- data/ext/ruby_xnd/xnd/AUTHORS.txt +5 -0
- data/ext/ruby_xnd/xnd/INSTALL.txt +134 -0
- data/ext/ruby_xnd/xnd/LICENSE.txt +29 -0
- data/ext/ruby_xnd/xnd/MANIFEST.in +3 -0
- data/ext/ruby_xnd/xnd/Makefile.in +80 -0
- data/ext/ruby_xnd/xnd/README.rst +44 -0
- data/ext/ruby_xnd/xnd/config.guess +1530 -0
- data/ext/ruby_xnd/xnd/config.h.in +22 -0
- data/ext/ruby_xnd/xnd/config.sub +1782 -0
- data/ext/ruby_xnd/xnd/configure +4867 -0
- data/ext/ruby_xnd/xnd/configure.ac +164 -0
- data/ext/ruby_xnd/xnd/doc/Makefile +14 -0
- data/ext/ruby_xnd/xnd/doc/_static/copybutton.js +66 -0
- data/ext/ruby_xnd/xnd/doc/conf.py +26 -0
- data/ext/ruby_xnd/xnd/doc/index.rst +44 -0
- data/ext/ruby_xnd/xnd/doc/libxnd/data-structures.rst +186 -0
- data/ext/ruby_xnd/xnd/doc/libxnd/functions.rst +148 -0
- data/ext/ruby_xnd/xnd/doc/libxnd/index.rst +25 -0
- data/ext/ruby_xnd/xnd/doc/releases/index.rst +34 -0
- data/ext/ruby_xnd/xnd/doc/xnd/align-pack.rst +96 -0
- data/ext/ruby_xnd/xnd/doc/xnd/buffer-protocol.rst +42 -0
- data/ext/ruby_xnd/xnd/doc/xnd/index.rst +30 -0
- data/ext/ruby_xnd/xnd/doc/xnd/quickstart.rst +62 -0
- data/ext/ruby_xnd/xnd/doc/xnd/types.rst +674 -0
- data/ext/ruby_xnd/xnd/install-sh +527 -0
- data/ext/ruby_xnd/xnd/libxnd/Makefile.in +102 -0
- data/ext/ruby_xnd/xnd/libxnd/Makefile.vc +112 -0
- data/ext/ruby_xnd/xnd/libxnd/bitmaps.c +345 -0
- data/ext/ruby_xnd/xnd/libxnd/contrib.h +313 -0
- data/ext/ruby_xnd/xnd/libxnd/copy.c +944 -0
- data/ext/ruby_xnd/xnd/libxnd/equal.c +1216 -0
- data/ext/ruby_xnd/xnd/libxnd/inline.h +154 -0
- data/ext/ruby_xnd/xnd/libxnd/overflow.h +147 -0
- data/ext/ruby_xnd/xnd/libxnd/split.c +286 -0
- data/ext/ruby_xnd/xnd/libxnd/tests/Makefile.in +39 -0
- data/ext/ruby_xnd/xnd/libxnd/tests/Makefile.vc +44 -0
- data/ext/ruby_xnd/xnd/libxnd/tests/README.txt +2 -0
- data/ext/ruby_xnd/xnd/libxnd/tests/runtest.c +101 -0
- data/ext/ruby_xnd/xnd/libxnd/tests/test.h +48 -0
- data/ext/ruby_xnd/xnd/libxnd/tests/test_fixed.c +108 -0
- data/ext/ruby_xnd/xnd/libxnd/xnd.c +1304 -0
- data/ext/ruby_xnd/xnd/libxnd/xnd.h +449 -0
- data/ext/ruby_xnd/xnd/python/test_xnd.py +3144 -0
- data/ext/ruby_xnd/xnd/python/xnd/__init__.py +290 -0
- data/ext/ruby_xnd/xnd/python/xnd/_xnd.c +2822 -0
- data/ext/ruby_xnd/xnd/python/xnd/contrib/pretty.py +850 -0
- data/ext/ruby_xnd/xnd/python/xnd/docstrings.h +129 -0
- data/ext/ruby_xnd/xnd/python/xnd/pyxnd.h +200 -0
- data/ext/ruby_xnd/xnd/python/xnd/util.h +182 -0
- data/ext/ruby_xnd/xnd/python/xnd_randvalue.py +1121 -0
- data/ext/ruby_xnd/xnd/python/xnd_support.py +106 -0
- data/ext/ruby_xnd/xnd/setup.py +303 -0
- data/ext/ruby_xnd/xnd/vcbuild/INSTALL.txt +42 -0
- data/ext/ruby_xnd/xnd/vcbuild/runtest32.bat +16 -0
- data/ext/ruby_xnd/xnd/vcbuild/runtest64.bat +14 -0
- data/ext/ruby_xnd/xnd/vcbuild/vcbuild32.bat +29 -0
- data/ext/ruby_xnd/xnd/vcbuild/vcbuild64.bat +29 -0
- data/ext/ruby_xnd/xnd/vcbuild/vcclean.bat +13 -0
- data/ext/ruby_xnd/xnd/vcbuild/vcdistclean.bat +14 -0
- data/lib/ruby_xnd.so +0 -0
- data/lib/xnd.rb +306 -0
- data/lib/xnd/monkeys.rb +29 -0
- data/lib/xnd/version.rb +6 -0
- data/spec/debug_spec.rb +9 -0
- data/spec/gc_guard_spec.rb +10 -0
- data/spec/leakcheck.rb +9 -0
- data/spec/spec_helper.rb +877 -0
- data/spec/type_inference_spec.rb +81 -0
- data/spec/xnd_spec.rb +2921 -0
- data/xnd.gemspec +47 -0
- 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
|
+
|