Coverage for cuda/core/_memoryview.pyx: 65.49%

710 statements  

« prev     ^ index     » next       coverage.py v7.14.1, created at 2026-06-13 01:38 +0000

1# SPDX-FileCopyrightText: Copyright (c) 2024-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. 

2# 

3# SPDX-License-Identifier: Apache-2.0 

4  

5from __future__ import annotations 

6  

7from ._dlpack cimport * 

8from ._dlpack import classify_dl_device 

9from libc.stdint cimport intptr_t 

10from cuda.core._layout cimport _StridedLayout, get_strides_ptr 

11from cuda.core._stream import Stream 

12  

13import ctypes 

14import functools 

15import sys 

16import warnings 

17from collections.abc import Callable # no-cython-lint # used in string annotations below 

18from typing import Any # no-cython-lint # used in string annotations below 

19  

20import numpy 

21  

22from cuda.bindings cimport cydriver 

23from cuda.core._resource_handles cimport ( 

24 EventHandle, 

25 create_event_handle_noctx, 

26 as_cu, 

27) 

28  

29from cuda.core._utils.cuda_utils import handle_return, driver 

30from cuda.core._utils.cuda_utils cimport HANDLE_RETURN 

31  

32  

33from cuda.core._memory import Buffer 

34  

35  

36# --------------------------------------------------------------------------- 

37# Lazy tensor bridge (avoids loading _tensor_bridge.so until torch is used) 

38# --------------------------------------------------------------------------- 

39  

40cdef object _tensor_bridge = None 

41# Cache: type(obj) -> True/False for the torch tensor check. 

42# Once a type is seen, we never re-check. 

43cdef dict _torch_type_cache = {} 

44# Tri-state: None = not checked, True/False = result of version check 

45cdef object _torch_version_ok = None 

46  

47cdef inline bint _torch_version_check(): 

48 """Return True if 2.3 <= torch <= 2.12 (known AOTI ABI range). Memoized. 

49  

50 Lower bound: AOTI functions we use were introduced in PyTorch 2.3. 

51 Upper bound: the ``pyobj_to_aten_handle`` trick relies on the 

52 THPVariable struct layout (PyObject_HEAD followed by at::Tensor cdata) 

53 and the identity ``AtenTensorHandle == at::Tensor*``. Both are 

54 undocumented internals that could change in a future PyTorch version. 

55 We cap at the latest version we have tested against; unknown versions 

56 fall back to the standard DLPack/CAI paths. Bump the upper bound 

57 after verifying a new PyTorch release. 

58 """ 

59 global _torch_version_ok 

60 if _torch_version_ok is not None: 

61 return <bint>_torch_version_ok 

62 torch = sys.modules.get("torch") 

63 if torch is None: 

64 _torch_version_ok = False 

65 return False 

66 try: 

67 major, minor = int(torch.__version__.split(".")[0]), \ 

68 int(torch.__version__.split(".")[1]) 

69 _torch_version_ok = (2, 3) <= (major, minor) <= (2, 12) 

70 except (ValueError, IndexError): 

71 _torch_version_ok = False 

72 return <bint>_torch_version_ok 

73  

74  

75cdef inline bint _is_torch_tensor(object obj): 

76 cdef type tp = type(obj) 2E F G Y t n o p q r u v w x y c d b Z 0 ! 1 # 2 $ 3 4 5 6 % ' ( 7 ) * + , - . / : 8 yb; wb9 W X a H I J K L M N O P Q R S T U m V l f g h i j k

77 cdef object cached = _torch_type_cache.get(tp) 2E F G Y t n o p q r u v w x y c d b Z 0 ! 1 # 2 $ 3 4 5 6 % ' ( 7 ) * + , - . / : 8 yb; wb9 W X a H I J K L M N O P Q R S T U m V l f g h i j k

78 if cached is not None: 2E F G Y t n o p q r u v w x y c d b Z 0 ! 1 # 2 $ 3 4 5 6 % ' ( 7 ) * + , - . / : 8 yb; wb9 W X a H I J K L M N O P Q R S T U m V l f g h i j k

79 return <bint>cached 2G Y n o p q r u v w x y c d b Z 0 ! 1 # 2 $ 3 4 5 6 % ' ( 7 ) * + , - . / : 8 ybW X a H I J K L M N O P Q R S T U m V l f g h i j k

80 cdef str mod = tp.__module__ or "" 2E F t ; wb9

81 cdef bint result = mod.startswith("torch") and hasattr(obj, "data_ptr") \ 2E F t ; wb9

82 and _torch_version_check() 

83 _torch_type_cache[tp] = result 2E F t ; wb9

84 return result 2E F t ; wb9

85  

86  

87cdef object _get_tensor_bridge(): 

88 """Bootstrap AOTI symbols, then import _tensor_bridge on first use.""" 

89 global _tensor_bridge 

90 if _tensor_bridge is not None: 

91 return _tensor_bridge 

92 torch_C = sys.modules.get("torch._C") 

93 if torch_C is None: 

94 raise RuntimeError( 

95 "torch._C is not loaded; cannot initialise the tensor bridge. " 

96 "Make sure PyTorch is imported before passing a torch.Tensor.") 

97 ctypes.CDLL(torch_C.__file__, mode=ctypes.RTLD_GLOBAL) 

98 from cuda.core import _tensor_bridge as tb 

99 _tensor_bridge = tb 

100 return _tensor_bridge 

101  

102  

103try: 

104 from ml_dtypes import bfloat16 

105except ImportError: 

106 bfloat16 = None 

107  

108# TODO(leofang): support NumPy structured dtypes 

109  

110  

111cdef extern from "Python.h": 

112 ctypedef struct PyTypeObject: 

113 void* tp_dict 

114 void PyType_Modified(PyTypeObject*) 

115  

116  

117cdef DLPackExchangeAPI _SMV_DLPACK_EXCHANGE_API 

118cdef bint _SMV_DLPACK_EXCHANGE_API_INITED = False 

119_SMV_DLPACK_EXCHANGE_API_CAPSULE = cpython.PyCapsule_New( 

120 <void*>&_SMV_DLPACK_EXCHANGE_API, 

121 b"dlpack_exchange_api", 

122 NULL, 

123) 

124  

125  

126cdef class StridedMemoryView: 

127 """A class holding metadata of a strided dense array/tensor. 

128  

129 A :obj:`StridedMemoryView` instance can be created in three ways: 

130  

131 1. Using the :obj:`args_viewable_as_strided_memory` decorator (recommended) 

132 2. Explicit construction relying on DLPack or CUDA Array Interface, see below. 

133 3. From :obj:`~_memory.Buffer` and shape and size tuples (see 

134 :meth:`from_buffer` classmethod) 

135  

136 ``StridedMemoryView(obj, stream_ptr)`` can be used to create a view from 

137 objects supporting either DLPack (up to v1.0) or CUDA Array Interface 

138 (CAI) v3. When wrapping an arbitrary object it will try the DLPack protocol 

139 first, then the CAI protocol. A :obj:`BufferError` is raised if neither is 

140 supported. 

141  

142 Since either way would take a consumer stream, for DLPack it is passed to 

143 ``obj.__dlpack__()`` as-is (except for :obj:`None`, see below); for CAI, a 

144 stream order will be established between the consumer stream and the 

145 producer stream (from ``obj.__cuda_array_interface__()["stream"]``), as if 

146 ``cudaStreamWaitEvent`` is called by this method. 

147  

148 To opt-out of the stream ordering operation in either DLPack or CAI, 

149 please pass ``stream_ptr=-1``. Note that this deviates (on purpose) 

150 from the semantics of ``obj.__dlpack__(stream=None, ...)`` since ``cuda.core`` 

151 does not encourage using the (legacy) default/null stream, but is 

152 consistent with the CAI's semantics. For DLPack, ``stream=-1`` will be 

153 internally passed to ``obj.__dlpack__()`` instead. 

154  

155 Parameters 

156 ---------- 

157 obj : Any 

158 Any objects that supports either DLPack (up to v1.0) or CUDA Array 

159 Interface (v3). 

160 stream_ptr: int 

161 The pointer address (as Python `int`) to the **consumer** stream. 

162 Stream ordering will be properly established unless ``-1`` is passed. 

163  

164  

165 Attributes 

166 ----------- 

167 ptr : int 

168 Pointer to the tensor buffer (as a Python `int`). 

169 device_id : int 

170 The device ID for where the tensor is located. It is -1 for CPU tensors 

171 (meaning those only accessible from the host). 

172 is_device_accessible : bool 

173 Whether the tensor data can be accessed on the GPU. 

174 readonly: bool 

175 Whether the tensor data can be modified in place. 

176 exporting_obj : Any 

177 A reference to the original tensor object that is being viewed. 

178 If the view is created with :meth:`from_buffer`, 

179 it will be the Buffer instance passed to the method. 

180  

181 """ 

182 def __init__(self, obj: object = None, stream_ptr: int | None = None) -> None: 

183 cdef str clsname = self.__class__.__name__ 2z A B C D Ab

184 if obj is not None: 2z A B C D Ab

185 # populate self's attributes 

186 if check_has_dlpack(obj): 1zABCD

187 warnings.warn( 1zABCD

188 f"Constructing a {clsname} directly from a DLPack-supporting object is deprecated; " 1zABCD

189 "Use `StridedMemoryView.from_dlpack` or `StridedMemoryView.from_any_interface` instead.", 

190 DeprecationWarning, 1zABCD

191 stacklevel=2, 

192 ) 

193 view_as_dlpack(obj, stream_ptr, self) 1zABCD

194 else: 

195 warnings.warn( 

196 f"Constructing a {clsname} directly from a CUDA-array-interface-supporting object is deprecated; " 

197 "Use `StridedMemoryView.from_cuda_array_interface` or `StridedMemoryView.from_any_interface` instead.", 

198 DeprecationWarning, 

199 stacklevel=2, 

200 ) 

201 view_as_cai(obj, stream_ptr, self) 

202 else: 

203 warnings.warn( 2Ab

204 f"Constructing an empty {clsname} is deprecated; " 2Ab

205 "use one of the classmethods `from_dlpack`, `from_cuda_array_interface` or `from_any_interface` " 

206 "to construct a StridedMemoryView from an object", 

207 DeprecationWarning, 2Ab

208 stacklevel=2, 

209 ) 

210  

211 @classmethod 

212 def from_dlpack(cls, obj: object, stream_ptr: int | None=None) -> StridedMemoryView: 

213 """Create a view from an object supporting the `DLPack <https://dmlc.github.io/dlpack/latest/>`_ protocol. 

214  

215 Parameters 

216 ---------- 

217 obj : object 

218 An object implementing the `DLPack <https://dmlc.github.io/dlpack/latest/>`_ protocol 

219 (via ``__dlpack__``). 

220 stream_ptr : int, optional 

221 Stream pointer for synchronization. If ``None``, no synchronization is performed. 

222 """ 

223 cdef StridedMemoryView buf = StridedMemoryView.__new__(cls) 1eEFGYtnopqruvwxycdbWXaHIJKLMNOPQRSTUmVlfghijk

224 if _is_torch_tensor(obj): 1EFGYtnopqruvwxycdbWXaHIJKLMNOPQRSTUmVlfghijk

225 _get_tensor_bridge().view_as_torch_tensor(obj, stream_ptr, buf) 

226 return buf 

227 view_as_dlpack(obj, stream_ptr, buf) 1EFGYtnopqruvwxycdbWXaHIJKLMNOPQRSTUmVlfghijk

228 return buf 1EFGYtnopqruvwxycdbWXaHIJKLMNOPQRSTUmVlfghijk

229  

230 @classmethod 

231 def from_cuda_array_interface(cls, obj: object, stream_ptr: int | None=None) -> StridedMemoryView: 

232 """Create a view from an object supporting the `__cuda_array_interface__ <https://numba.readthedocs.io/en/stable/cuda/cuda_array_interface.html>`_ protocol. 

233  

234 Parameters 

235 ---------- 

236 obj : object 

237 An object implementing the `__cuda_array_interface__ <https://numba.readthedocs.io/en/stable/cuda/cuda_array_interface.html>`_ protocol. 

238 stream_ptr : int, optional 

239 Stream pointer for synchronization. If ``None``, no synchronization is performed. 

240 """ 

241 cdef StridedMemoryView buf = StridedMemoryView.__new__(cls) 2e ; wb9

242 if _is_torch_tensor(obj): 2; wb9

243 _get_tensor_bridge().view_as_torch_tensor(obj, stream_ptr, buf) 

244 return buf 

245 view_as_cai(obj, stream_ptr, buf) 2; wb9

246 return buf 1e;9

247  

248 @classmethod 

249 def from_array_interface(cls, obj: object) -> StridedMemoryView: 

250 """Create a view from an object supporting the `__array_interface__ <https://numpy.org/doc/stable/reference/arrays.interface.html>`_ protocol. 

251  

252 Parameters 

253 ---------- 

254 obj : object 

255 An object implementing the `__array_interface__ <https://numpy.org/doc/stable/reference/arrays.interface.html>`_ protocol (e.g., a numpy array). 

256 """ 

257 cdef StridedMemoryView buf = StridedMemoryView.__new__(cls) 2Z 0 ! 1 # 2 $ 3 4 5 6 % ' ( 7 ) * + , - . / : 8 yb

258 if _is_torch_tensor(obj): 2e Z 0 ! 1 # 2 $ 3 4 5 6 % ' ( 7 ) * + , - . / : 8 yb

259 _get_tensor_bridge().view_as_torch_tensor(obj, None, buf) 

260 return buf 

261 view_as_array_interface(obj, buf) 2Z 0 ! 1 # 2 $ 3 4 5 6 % ' ( 7 ) * + , - . / : 8 yb

262 return buf 1Z0!1#2$3456%'(7)*+,-./:8

263  

264 @classmethod 

265 def from_any_interface(cls, obj: object, stream_ptr: int | None = None) -> StridedMemoryView: 

266 """Create a view by automatically selecting the best available protocol. 

267  

268 Tries `DLPack <https://dmlc.github.io/dlpack/latest/>`_ first, then falls back to 

269 `__cuda_array_interface__ <https://numba.readthedocs.io/en/stable/cuda/cuda_array_interface.html>`_. 

270 ``torch.Tensor`` objects are transparently handled via a fast AOTI path 

271 regardless of which protocol is selected. 

272  

273 Parameters 

274 ---------- 

275 obj : object 

276 An object implementing `DLPack <https://dmlc.github.io/dlpack/latest/>`_ or 

277 `__cuda_array_interface__ <https://numba.readthedocs.io/en/stable/cuda/cuda_array_interface.html>`_. 

278 stream_ptr : int, optional 

279 Stream pointer for synchronization. If ``None``, no synchronization is performed. 

280 """ 

281 if check_has_dlpack(obj): 2E F G Y t u v w x y Hbc d b W X a m V l

282 return cls.from_dlpack(obj, stream_ptr) 1EFGYtuvwxycdbWXamVl

283 return cls.from_cuda_array_interface(obj, stream_ptr) 

284  

285 @classmethod 

286 def from_buffer( 

287 cls, 

288 buffer : Buffer, 

289 shape : tuple[int, ...], 

290 strides : tuple[int, ...] | None = None, 

291 *, 

292 itemsize : int | None = None, 

293 dtype : numpy.dtype | None = None, 

294 is_readonly : bool = False 

295 ) -> StridedMemoryView: 

296 """ 

297 Creates a :obj:`StridedMemoryView` instance from a :obj:`~_memory.Buffer` and shape and strides tuples. 

298 The Buffer can be either allocation coming from a :obj:`MemoryResource` or an external allocation 

299 wrapped in a :obj:`~_memory.Buffer` object with ``Buffer.from_handle(ptr, size, owner=...)``. 

300  

301 .. caution:: 

302 When creating a :obj:`StridedMemoryView` from a :obj:`~_memory.Buffer`, 

303 no synchronization is performed. It is the user's responsibility to ensure 

304 the data in ``buffer`` is properly synchronized when consuming the view. 

305  

306 Parameters 

307 ---------- 

308 buffer : :obj:`~_memory.Buffer` 

309 The buffer to create the view from. 

310 shape : :obj:`tuple` 

311 The layout describing the shape, strides and itemsize of the elements in 

312 the buffer. 

313 strides : :obj:`tuple` 

314 The layout describing the shape, strides and itemsize of the elements in 

315 the buffer. 

316 dtype : :obj:`numpy.dtype` 

317 Optional dtype. 

318 If specified, the dtype's itemsize must match the layout's itemsize. 

319 is_readonly : bool, optional 

320 Whether the mark the view as readonly. 

321 """ 

322 cdef StridedMemoryView view = StridedMemoryView.__new__(cls) 2DbEb@ [ ] ^ _ ` { | } ~ abbbcbdbebfbgbhbibjbkblbmbnbobpbqbrbsbtbubvbBbFbCb= ? zbxbs

323 if itemsize is None and dtype is None: 2DbEb@ [ ] ^ _ ` { | } ~ abbbcbdbebfbgbhbibjbkblbmbnbobpbqbrbsbtbubvbBbFbCb= ? zbxbs

324 raise ValueError("Either itemsize or dtype must be specified") 2Fb

325 if itemsize is not None and dtype is not None and itemsize != dtype.itemsize: 2DbEb@ [ ] ^ _ ` { | } ~ abbbcbdbebfbgbhbibjbkblbmbnbobpbqbrbsbtbubvbBbCb= ? zbxbs

326 raise ValueError( 2Cb

327 f"itemsize ({itemsize}) does not match dtype.itemsize ({dtype.itemsize})" 2Cb

328 ) 

329 # (itemsize is None XOR dtype is None) OR they are equal 

330 view_buffer_strided( 2@ [ ] ^ _ ` { | } ~ abbbcbdbebfbgbhbibjbkblbmbnbobpbqbrbsbtbubvbBb= ? zbxbs

331 view, 

332 buffer, 

333 _StridedLayout(shape=shape, strides=strides, itemsize=getattr(dtype, "itemsize", itemsize)), 2DbEb@ [ ] ^ _ ` { | } ~ abbbcbdbebfbgbhbibjbkblbmbnbobpbqbrbsbtbubvbBb= ? zbxbs

334 dtype, 

335 is_readonly, 

336 ) 

337 return view 2@ [ ] ^ _ ` { | } ~ abbbcbdbebfbgbhbibjbkblbmbnbobpbqbrbsbtbubvb= ? xbs

338  

339 def __dealloc__(self) -> None: 

340 if self.dl_tensor == NULL: 2DbEbE F G Y t n o p q r u v w x y z A B C D c d b Z 0 ! 1 # 2 $ 3 4 5 6 % ' ( 7 ) * + , - . / : 8 yb@ [ ] ^ _ ` { | } ~ abbbcbdbebfbgbhbibjbkblbmbnbobpbqbrbsbtbubvbBbFbCb= ? zbxb; wb9 W X a s H I J K L M N O P Q R S T U Abm V l f g h i j k

341 return 2DbEbc d b Z 0 ! 1 # 2 $ 3 4 5 6 % ' ( 7 ) * + , - . / : 8 yb@ [ ] ^ _ ` { | } ~ abbbcbdbebfbgbhbibjbkblbmbnbobpbqbrbsbtbubvbBbFbCb= ? zbxb; wb9 s AbV l f g h i j k

342  

343 if cpython.PyCapsule_IsValid( 1EFGYtnopqruvwxyzABCDcdbWXaHIJKLMNOPQRSTUmVlfghijk

344 self.metadata, DLPACK_VERSIONED_TENSOR_USED_NAME): 1EFGYtnopqruvwxyzABCDcdbWXaHIJKLMNOPQRSTUmVlfghijk

345 data = cpython.PyCapsule_GetPointer( 1EFGYtnopqruvwxyzABCDcdbWXaHIJKLMNOPQRSTUmVlfghijk

346 self.metadata, DLPACK_VERSIONED_TENSOR_USED_NAME) 1EFGYtnopqruvwxyzABCDcdbWXaHIJKLMNOPQRSTUmVlfghijk

347 dlm_tensor_ver = <DLManagedTensorVersioned*>data 1EFGYtnopqruvwxyzABCDcdbWXaHIJKLMNOPQRSTUmVlfghijk

348 dlm_tensor_ver.deleter(dlm_tensor_ver) 1EFGYtnopqruvwxyzABCDcdbWXaHIJKLMNOPQRSTUmVlfghijk

349 elif cpython.PyCapsule_IsValid( 

350 self.metadata, DLPACK_TENSOR_USED_NAME): 

351 data = cpython.PyCapsule_GetPointer( 

352 self.metadata, DLPACK_TENSOR_USED_NAME) 

353 dlm_tensor = <DLManagedTensor*>data 

354 dlm_tensor.deleter(dlm_tensor) 

355  

356 def view( 

357 self, layout : _StridedLayout | None = None, dtype : numpy.dtype | None = None 

358 ) -> StridedMemoryView: 

359 """ 

360 Creates a new view with adjusted layout and dtype. 

361 Same as calling :meth:`from_buffer` with the current buffer. 

362 """ 

363 cdef StridedMemoryView view = StridedMemoryView.__new__(self.__class__) 1cdb=?Vlfghijk

364 if layout is None and dtype is None: 1cdb=?Vlfghijk

365 return self 1eV

366 if layout is None: 1cdb=?lfghijk

367 layout = self.get_layout() 1cdbl

368 if dtype is None: 1cdb=?lfghijk

369 dtype = self.get_dtype() 1=?fghijk

370 view_buffer_strided(view, self.get_buffer(), layout, dtype, self.readonly) 1ecdb=?lfghijk

371 return view 1cdb=?lfghijk

372  

373 def as_tensor_map( 

374 self, 

375 box_dim: tuple[int, ...] | None = None, 

376 *, 

377 options: object = None, 

378 element_strides: tuple[int, ...] | None = None, 

379 data_type: object = None, 

380 interleave: object = None, 

381 swizzle: object = None, 

382 l2_promotion: object = None, 

383 oob_fill: object = None, 

384 ) -> object: 

385 """Create a tiled :obj:`TensorMapDescriptor` from this view. 

386  

387 This is the public entry point for creating tiled tensor map 

388 descriptors in ``cuda.core``. Pass either ``box_dim`` and the 

389 individual keyword arguments directly, or provide bundled tiled 

390 options via ``options=``. 

391 """ 

392 from cuda.core._tensor_map import TensorMapDescriptor 

393  

394 kwargs = {} 

395 if options is not None: 

396 kwargs["options"] = options 

397 if element_strides is not None: 

398 kwargs["element_strides"] = element_strides 

399 if data_type is not None: 

400 kwargs["data_type"] = data_type 

401 if interleave is not None: 

402 kwargs["interleave"] = interleave 

403 if swizzle is not None: 

404 kwargs["swizzle"] = swizzle 

405 if l2_promotion is not None: 

406 kwargs["l2_promotion"] = l2_promotion 

407 if oob_fill is not None: 

408 kwargs["oob_fill"] = oob_fill 

409 return TensorMapDescriptor._from_tiled(self, box_dim, **kwargs) 

410  

411 def copy_from( 

412 self, 

413 other: StridedMemoryView, 

414 stream: Stream, 

415 allocator: object = None, 

416 blocking: bool | None = None, 

417 ) -> None: 

418 """ 

419 Copies the data from the other view into this view. 

420  

421 The copy can be performed between following memory spaces: 

422 host-to-device, device-to-host, device-to-device (on the same device). 

423  

424 Parameters 

425 ---------- 

426 other : StridedMemoryView 

427 The view to copy data from. 

428 stream : Stream | None, optional 

429 The stream to schedule the copy on. 

430 allocator : MemoryResource | None, optional 

431 If temporary buffers are needed, the specified memory resources 

432 will be used to allocate the memory. If not specified, default 

433 resources will be used. 

434 blocking : bool | None, optional 

435 Whether the call should block until the copy is complete. 

436 * ``True``: the ``stream`` is synchronized with the host at the end of the call, 

437 blocking until the copy is complete. 

438 * ``False``: if possible, the call returns immediately once the copy is scheduled. 

439 However, in some cases of host-to-device or device-to-host copies, the call may 

440 still synchronize with the host if necessary. 

441 * ``None`` (default): 

442 * for device-to-device, it defaults to ``False`` (non-blocking), 

443 * for host-to-device or device-to-host, it defaults to ``True`` (blocking). 

444 """ 

445 raise NotImplementedError("Sorry, not supported: copy_from") 1W

446  

447 def copy_to( 

448 self, 

449 other: StridedMemoryView, 

450 stream: Stream | None = None, 

451 allocator: object = None, 

452 blocking: bool | None = None, 

453 ) -> None: 

454 """ 

455 Copies the data from this view into the ``other`` view. 

456  

457 For details, see :meth:`copy_from`. 

458 """ 

459 raise NotImplementedError("Sorry, not supported: copy_to") 1X

460  

461 def __dlpack__( 

462 self, 

463 *, 

464 stream: int | None = None, 

465 max_version: tuple[int, int] | None = None, 

466 dl_device: tuple[int, int] | None = None, 

467 copy: bool | None = None, 

468 ) -> object: 

469 # Similar to Buffer.__dlpack__: no implicit synchronization is performed. 

470 if dl_device is not None: 1Gcdbas

471 raise BufferError("Sorry, not supported: dl_device other than None") 1G

472 if copy is True: 1Gcdbas

473 raise BufferError("Sorry, not supported: copy=True") 1G

474  

475 cdef bint versioned 

476 if max_version is None: 1Gcdbas

477 versioned = False 1cdbs

478 else: 

479 if not isinstance(max_version, tuple) or len(max_version) != 2: 1Ga

480 raise BufferError(f"Expected max_version tuple[int, int], got {max_version}") 1G

481 versioned = max_version >= (1, 0) 1ea

482  

483 # NOTE: stream is accepted for protocol compatibility but not used. 

484 cdef object capsule = _smv_make_py_capsule(self, versioned) 1cdbas

485 return capsule 1a

486  

487 def __dlpack_device__(self) -> tuple[int, int]: 

488 cdef _DLDeviceType device_type 

489 cdef int32_t device_id 

490 _smv_get_dl_device(self, &device_type, &device_id) 1EFa

491 return (<int>device_type, int(device_id)) 1EFa

492  

493 @property 

494 def _layout(self) -> _StridedLayout: 

495 """ 

496 The layout of the tensor. For StridedMemoryView created from DLPack or CAI, 

497 the layout is inferred from the tensor object's metadata. 

498 """ 

499 return self.get_layout() 2@ [ ] ^ _ ` { | } ~ abbbcbdbebfbgbhbibjbkblbmbnbobpbqbrbsbtbubvb= ? Abl f g h i j k

500  

501 @property 

502 def size(self) -> int: 

503 return self.get_layout().get_volume() 1nopqruvwxyzABCDZ0!1#2$3456%'(7)*+,-./:8;

504  

505 @property 

506 def shape(self) -> tuple[int, ...]: 

507 """ 

508 Shape of the tensor. 

509 """ 

510 return self.get_layout().get_shape_tuple() 2t n o p q r u v w x y z A B C D Z 0 ! 1 # 2 $ 3 4 5 6 % ' ( 7 ) * + , - . / : 8 @ [ ] ^ _ ` { | } ~ abbbcbdbebfbgbhbibjbkblbmbnbobpbqbrbsbtbubvb= ? ; 9 m f g h i j k

511  

512 @property 

513 def strides(self) -> tuple[int, ...] | None: 

514 """ 

515 Strides of the tensor (in **counts**, not bytes). 

516 """ 

517 return self.get_layout().get_strides_tuple() 2t n o p q r u v w x y z A B C D Z 0 ! 1 # 2 $ 3 4 5 6 % ' ( 7 ) * + , - . / : 8 @ [ ] ^ _ ` { | } ~ abbbcbdbebfbgbhbibjbkblbmbnbobpbqbrbsbtbubvb; 9 m

518  

519 @property 

520 def dtype(self) -> numpy.dtype | None: 

521 """ 

522 Data type of the tensor. 

523  

524 Supports standard NumPy dtypes as well as narrow data types (e.g., ``bfloat16``) 

525 when the optional `ml_dtypes <https://github.com/jax-ml/ml_dtypes>`_ package is 

526 installed. If ``ml_dtypes`` is not available and such a tensor is encountered, 

527 a :obj:`NotImplementedError` will be raised. 

528 """ 

529 return self.get_dtype() 2Z 0 ! 1 # 2 $ 3 4 5 6 % ' ( 7 ) * + , - . / : 8 @ [ ] ^ _ ` { | } ~ abbbcbdbebfbgbhbibjbkblbmbnbobpbqbrbsbtbubvbxbH I J K L M N O P Q R S T U m l f g h i j k

530  

531 def __repr__(self) -> str: 

532 return (f"StridedMemoryView(ptr={self.ptr},\n" 1m

533 + f" shape={self.shape},\n" 1m

534 + f" strides={self.strides},\n" 1m

535 + f" itemsize={self._layout.itemsize},\n" 1m

536 + f" dtype={get_simple_repr(self.dtype)},\n" 1m

537 + f" device_id={self.device_id},\n" 1m

538 + f" is_device_accessible={self.is_device_accessible},\n" 1m

539 + f" readonly={self.readonly},\n" 1m

540 + f" exporting_obj={get_simple_repr(self.exporting_obj)})") 1m

541  

542 cdef inline _StridedLayout get_layout(self): 

543 if self._layout is None: 2t n o p q r u v w x y z A B C D c d b Z 0 ! 1 # 2 $ 3 4 5 6 % ' ( 7 ) * + , - . / : 8 yb@ [ ] ^ _ ` { | } ~ abbbcbdbebfbgbhbibjbkblbmbnbobpbqbrbsbtbubvb= ? ; wb9 a s Abm l f g h i j k

544 if self.dl_tensor: 2t n o p q r u v w x y z A B C D c d b Z 0 ! 1 # 2 $ 3 4 5 6 % ' ( 7 ) * + , - . / : 8 yb; wb9 a Abm l f g h i j k

545 self._layout = layout_from_dlpack(self.dl_tensor) 1tnopqruvwxyzABCDcdbamlfghijk

546 elif self.metadata is not None: 2Z 0 ! 1 # 2 $ 3 4 5 6 % ' ( 7 ) * + , - . / : 8 yb; wb9 Ab

547 self._layout = layout_from_cai(self.metadata) 2Z 0 ! 1 # 2 $ 3 4 5 6 % ' ( 7 ) * + , - . / : 8 yb; wb9

548 else: 

549 raise ValueError("Cannot infer layout from the exporting object") 2Ab

550 return self._layout 2t n o p q r u v w x y z A B C D c d b Z 0 ! 1 # 2 $ 3 4 5 6 % ' ( 7 ) * + , - . / : 8 @ [ ] ^ _ ` { | } ~ abbbcbdbebfbgbhbibjbkblbmbnbobpbqbrbsbtbubvb= ? ; 9 a s m l f g h i j k

551  

552 cdef inline object get_buffer(self): 

553 """ 

554 Returns Buffer instance with the underlying data. 

555 If the SMV was created from a Buffer, it will return the same Buffer instance. 

556 Otherwise, it will create a new instance with owner set to the exporting object. 

557 """ 

558 if self._buffer is None: 1cdb=?lfghijk

559 if isinstance(self.exporting_obj, Buffer): 1cdblfghijk

560 self._buffer = self.exporting_obj 

561 else: 

562 self._buffer = Buffer.from_handle(self.ptr, 0, owner=self.exporting_obj) 1cdblfghijk

563 return self._buffer 1cdb=?lfghijk

564  

565 cdef inline object get_dtype(self): 

566 if self._dtype is None: 2c d b Z 0 ! 1 # 2 $ 3 4 5 6 % ' ( 7 ) * + , - . / : 8 @ [ ] ^ _ ` { | } ~ abbbcbdbebfbgbhbibjbkblbmbnbobpbqbrbsbtbubvb= ? xba s H I J K L M N O P Q R S T U m l f g h i j k

567 if self.dl_tensor != NULL: 1Z0!1#2$3456%'(7)*+,-./:8asHIJKLMNOPQRSTUmfghijk

568 self._dtype = dtype_dlpack_to_numpy(&self.dl_tensor.dtype) 1aHIJKLMNOPQRSTUmfghijk

569 elif isinstance(self.metadata, int): 1Z0!1#2$3456%'(7)*+,-./:8s

570 # AOTI dtype code stored by the torch tensor bridge 

571 self._dtype = _get_tensor_bridge().resolve_aoti_dtype( 

572 self.metadata) 

573 elif self.metadata is not None: 1Z0!1#2$3456%'(7)*+,-./:8s

574 self._dtype = _typestr2dtype(self.metadata["typestr"]) 1Z0!1#2$3456%'(7)*+,-./:8

575 return self._dtype 2c d b Z 0 ! 1 # 2 $ 3 4 5 6 % ' ( 7 ) * + , - . / : 8 @ [ ] ^ _ ` { | } ~ abbbcbdbebfbgbhbibjbkblbmbnbobpbqbrbsbtbubvb= ? xba s H I J K L M N O P Q R S T U m l f g h i j k

576  

577  

578cdef void _smv_pycapsule_deleter(object capsule) noexcept: 

579 cdef DLManagedTensor* dlm_tensor 

580 cdef DLManagedTensorVersioned* dlm_tensor_ver 

581 # Do not invoke the deleter on a used capsule. 

582 if cpython.PyCapsule_IsValid(capsule, DLPACK_TENSOR_UNUSED_NAME): 1a

583 dlm_tensor = <DLManagedTensor*>( 

584 cpython.PyCapsule_GetPointer(capsule, DLPACK_TENSOR_UNUSED_NAME) 

585 ) 

586 if dlm_tensor.deleter: 

587 dlm_tensor.deleter(dlm_tensor) 

588 elif cpython.PyCapsule_IsValid(capsule, DLPACK_VERSIONED_TENSOR_UNUSED_NAME): 1a

589 dlm_tensor_ver = <DLManagedTensorVersioned*>( 

590 cpython.PyCapsule_GetPointer(capsule, DLPACK_VERSIONED_TENSOR_UNUSED_NAME) 

591 ) 

592 if dlm_tensor_ver.deleter: 

593 dlm_tensor_ver.deleter(dlm_tensor_ver) 

594  

595  

596cdef inline void _smv_release_export_resources(void* manager_ctx, int64_t* shape_ptr) noexcept with gil: 

597 if shape_ptr: 1cdbas

598 stdlib.free(shape_ptr) 1a

599 if manager_ctx: 1cdbas

600 cpython.Py_DECREF(<object>manager_ctx) 1cdbas

601  

602  

603cdef void _smv_deleter(DLManagedTensor* tensor) noexcept with gil: 

604 if tensor: 1cdbs

605 _smv_release_export_resources(tensor.manager_ctx, tensor.dl_tensor.shape) 1cdbs

606 tensor.manager_ctx = NULL 1cdbs

607 stdlib.free(tensor) 1cdbs

608  

609  

610cdef void _smv_versioned_deleter(DLManagedTensorVersioned* tensor) noexcept with gil: 

611 if tensor: 1cdbas

612 _smv_release_export_resources(tensor.manager_ctx, tensor.dl_tensor.shape) 1a

613 tensor.manager_ctx = NULL 1ea

614 stdlib.free(tensor) 1a

615  

616  

617cdef inline DLManagedTensorVersioned* _smv_allocate_dlm_tensor_versioned() except? NULL: 

618 cdef DLManagedTensorVersioned* dlm_tensor_ver = NULL 1a

619 dlm_tensor_ver = <DLManagedTensorVersioned*>stdlib.malloc(sizeof(DLManagedTensorVersioned)) 1a

620 if dlm_tensor_ver == NULL: 1a

621 raise MemoryError() 

622 dlm_tensor_ver.dl_tensor.shape = NULL 1a

623 dlm_tensor_ver.manager_ctx = NULL 1a

624 return dlm_tensor_ver 1a

625  

626  

627cdef inline DLManagedTensor* _smv_allocate_dlm_tensor() except? NULL: 

628 cdef DLManagedTensor* dlm_tensor = NULL 1cdbs

629 dlm_tensor = <DLManagedTensor*>stdlib.malloc(sizeof(DLManagedTensor)) 1cdbs

630 if dlm_tensor == NULL: 1cdbs

631 raise MemoryError() 

632 dlm_tensor.dl_tensor.shape = NULL 1ecdbs

633 dlm_tensor.manager_ctx = NULL 1cdbs

634 return dlm_tensor 1cdbs

635  

636  

637cdef inline int _smv_dtype_numpy_to_dlpack(object dtype_obj, DLDataType* out_dtype) except -1: 

638 cdef object np_dtype = numpy.dtype(dtype_obj) 1cdba

639 if np_dtype.fields is not None: 1cdba

640 raise BufferError("Structured dtypes are not supported for DLPack export") 1ed

641 if not np_dtype.isnative and np_dtype.byteorder not in ("=", "|"): 1cba

642 raise BufferError("Non-native-endian dtypes are not supported for DLPack export") 1c

643  

644 cdef str kind = np_dtype.kind 1ba

645 cdef int bits = np_dtype.itemsize * 8 1ba

646 cdef uint8_t code 

647 if kind == "b": 1ba

648 if bits != 8: 

649 raise BufferError(f"Unsupported bool dtype itemsize: {np_dtype.itemsize}") 

650 code = <uint8_t>kDLBool 

651 elif kind == "i": 1ba

652 if bits not in (8, 16, 32, 64): 1a

653 raise BufferError(f"Unsupported signed integer dtype: {np_dtype}") 

654 code = <uint8_t>kDLInt 1a

655 elif kind == "u": 1b

656 if bits not in (8, 16, 32, 64): 

657 raise BufferError(f"Unsupported unsigned integer dtype: {np_dtype}") 

658 code = <uint8_t>kDLUInt 

659 elif kind == "f": 1b

660 if bits not in (16, 32, 64): 

661 raise BufferError(f"Unsupported floating dtype: {np_dtype}") 

662 code = <uint8_t>kDLFloat 

663 elif kind == "c": 1b

664 if bits not in (64, 128): 

665 raise BufferError(f"Unsupported complex dtype: {np_dtype}") 

666 code = <uint8_t>kDLComplex 

667 else: 

668 raise BufferError(f"Unsupported dtype for DLPack export: {np_dtype}") 1eb

669  

670 out_dtype.code = code 1a

671 out_dtype.bits = <uint8_t>bits 1a

672 out_dtype.lanes = <uint16_t>1 1a

673 return 0 1ea

674  

675  

676cdef inline int _smv_get_dl_device( 

677 StridedMemoryView view, 

678 _DLDeviceType* out_device_type, 

679 int32_t* out_device_id, 

680) except -1: 

681 cdef _DLDeviceType device_type 

682 cdef int32_t device_id 

683 cdef object buf 

684 if view.dl_tensor != NULL: 1EFa

685 device_type = view.dl_tensor.device.device_type 1EFa

686 if device_type == _kDLCUDA: 1EFa

687 device_id = view.dl_tensor.device.device_id 

688 else: 

689 # CPU, CUDAHost, and CUDAManaged use device_id=0 in DLPack. 

690 device_id = 0 1EFa

691 elif view.is_device_accessible: 

692 buf = view.get_buffer() 

693 dev_type, dev_id = classify_dl_device(buf) 

694 device_type = <_DLDeviceType>dev_type 

695 device_id = <int32_t>dev_id 

696 else: 

697 device_type = _kDLCPU 

698 device_id = 0 

699  

700 out_device_type[0] = device_type 1EFa

701 out_device_id[0] = device_id 1EFa

702 return 0 1EFa

703  

704  

705cdef inline int _smv_setup_dl_tensor_common( 

706 DLTensor* dl_tensor, 

707 StridedMemoryView view, 

708 _StridedLayout layout, 

709) except -1: 

710 cdef object dtype_obj = view.get_dtype() 1cdbas

711 if dtype_obj is None: 1cdbas

712 raise BufferError( 1s

713 "Cannot export StridedMemoryView via DLPack without dtype information; " 

714 "create the view with dtype specified." 

715 ) 

716 _smv_dtype_numpy_to_dlpack(dtype_obj, &dl_tensor.dtype) 1cdba

717 _smv_get_dl_device(view, &dl_tensor.device.device_type, &dl_tensor.device.device_id) 1a

718  

719 cdef int ndim = layout.base.ndim 1a

720 dl_tensor.ndim = ndim 1a

721 if layout.get_volume() == 0: 1a

722 dl_tensor.data = NULL 

723 else: 

724 dl_tensor.data = <void*><intptr_t>view.ptr 1a

725 dl_tensor.byte_offset = 0 1a

726 return 0 1a

727  

728  

729cdef inline int _smv_setup_dl_tensor(DLTensor* dl_tensor, StridedMemoryView view) except -1: 

730 cdef _StridedLayout layout = view.get_layout() 1cdbas

731 _smv_setup_dl_tensor_common(dl_tensor, view, layout) 1cdbas

732  

733 cdef int i 

734 cdef int64_t* shape_strides = NULL 1a

735 cdef int64_t* strides_src = NULL 1a

736 cdef int ndim = dl_tensor.ndim 1a

737 if ndim == 0: 1a

738 dl_tensor.shape = NULL 

739 dl_tensor.strides = NULL 

740 else: 

741 # DLPack v1.2+ requires non-NULL strides for ndim != 0. 

742 shape_strides = <int64_t*>stdlib.malloc(sizeof(int64_t) * 2 * ndim) 1a

743 if shape_strides == NULL: 1a

744 raise MemoryError() 

745 try: 1a

746 strides_src = get_strides_ptr(layout.base) 1a

747 for i in range(ndim): 1a

748 shape_strides[i] = layout.base.shape[i] 1a

749 shape_strides[i + ndim] = strides_src[i] 1a

750 except Exception: 

751 stdlib.free(shape_strides) 

752 raise 

753 dl_tensor.shape = shape_strides 1a

754 dl_tensor.strides = shape_strides + ndim 1a

755 return 0 1a

756  

757  

758cdef inline int _smv_setup_dltensor_borrowed(DLTensor* dl_tensor, StridedMemoryView view) except -1: 

759 cdef _StridedLayout layout = view.get_layout() 

760 _smv_setup_dl_tensor_common(dl_tensor, view, layout) 

761  

762 if dl_tensor.ndim == 0: 

763 dl_tensor.shape = NULL 

764 dl_tensor.strides = NULL 

765 else: 

766 dl_tensor.shape = layout.base.shape 

767 # For temporary/non-owning exchange we provide explicit strides. 

768 dl_tensor.strides = get_strides_ptr(layout.base) 

769 return 0 

770  

771  

772cdef inline int _smv_fill_managed_tensor_versioned( 

773 DLManagedTensorVersioned* dlm_tensor_ver, 

774 StridedMemoryView view, 

775) except -1: 

776 cpython.Py_INCREF(view) 1a

777 dlm_tensor_ver.manager_ctx = <void*>view 1a

778 dlm_tensor_ver.deleter = _smv_versioned_deleter 1a

779 dlm_tensor_ver.version.major = DLPACK_MAJOR_VERSION 1a

780 dlm_tensor_ver.version.minor = DLPACK_MINOR_VERSION 1a

781 dlm_tensor_ver.flags = DLPACK_FLAG_BITMASK_READ_ONLY if view.readonly else 0 1a

782 _smv_setup_dl_tensor(&dlm_tensor_ver.dl_tensor, view) 1a

783 return 0 1a

784  

785  

786cdef inline int _smv_fill_managed_tensor( 

787 DLManagedTensor* dlm_tensor, 

788 StridedMemoryView view, 

789) except -1: 

790 cpython.Py_INCREF(view) 1cdbs

791 dlm_tensor.manager_ctx = <void*>view 1cdbs

792 dlm_tensor.deleter = _smv_deleter 1cdbs

793 _smv_setup_dl_tensor(&dlm_tensor.dl_tensor, view) 1cdbs

794 return 0 

795  

796  

797cdef object _smv_make_py_capsule(StridedMemoryView view, bint versioned): 

798 cdef DLManagedTensor* dlm_tensor = NULL 1cdbas

799 cdef DLManagedTensorVersioned* dlm_tensor_ver = NULL 1cdbas

800 cdef object capsule = None 1cdbas

801 cdef void* tensor_ptr = NULL 1cdbas

802 cdef const char* capsule_name 

803 try: 1cdbas

804 if versioned: 1cdbas

805 dlm_tensor_ver = _smv_allocate_dlm_tensor_versioned() 1a

806 _smv_fill_managed_tensor_versioned(dlm_tensor_ver, view) 1a

807 tensor_ptr = <void*>dlm_tensor_ver 1a

808 capsule_name = DLPACK_VERSIONED_TENSOR_UNUSED_NAME 1a

809 else: 

810 dlm_tensor = _smv_allocate_dlm_tensor() 1cdbs

811 _smv_fill_managed_tensor(dlm_tensor, view) 1cdbs

812 tensor_ptr = <void*>dlm_tensor 

813 capsule_name = DLPACK_TENSOR_UNUSED_NAME 

814 capsule = cpython.PyCapsule_New(tensor_ptr, capsule_name, _smv_pycapsule_deleter) 1a

815 except Exception: 1cdbs

816 if capsule is None: 1cdbs

817 _smv_deleter(dlm_tensor) 1cdbs

818 _smv_versioned_deleter(dlm_tensor_ver) 1cdbs

819 raise 1cdbs

820 return capsule 1a

821  

822  

823cdef inline StridedMemoryView _smv_from_dlpack_capsule(object capsule, object exporting_obj): 

824 cdef void* data = NULL 

825 cdef DLTensor* dl_tensor = NULL 

826 cdef DLManagedTensorVersioned* dlm_tensor_ver = NULL 

827 cdef DLManagedTensor* dlm_tensor = NULL 

828 cdef bint is_readonly = False 

829 cdef const char* used_name = NULL 

830 if cpython.PyCapsule_IsValid(capsule, DLPACK_VERSIONED_TENSOR_UNUSED_NAME): 

831 data = cpython.PyCapsule_GetPointer(capsule, DLPACK_VERSIONED_TENSOR_UNUSED_NAME) 

832 dlm_tensor_ver = <DLManagedTensorVersioned*>data 

833 dl_tensor = &dlm_tensor_ver.dl_tensor 

834 is_readonly = bool((dlm_tensor_ver.flags & DLPACK_FLAG_BITMASK_READ_ONLY) != 0) 

835 used_name = DLPACK_VERSIONED_TENSOR_USED_NAME 

836 elif cpython.PyCapsule_IsValid(capsule, DLPACK_TENSOR_UNUSED_NAME): 

837 data = cpython.PyCapsule_GetPointer(capsule, DLPACK_TENSOR_UNUSED_NAME) 

838 dlm_tensor = <DLManagedTensor*>data 

839 dl_tensor = &dlm_tensor.dl_tensor 

840 is_readonly = False 

841 used_name = DLPACK_TENSOR_USED_NAME 

842 else: 

843 raise BufferError("Invalid DLPack capsule") 

844  

845 cpython.PyCapsule_SetName(capsule, used_name) 

846  

847 cdef StridedMemoryView view = StridedMemoryView.__new__(StridedMemoryView) 

848 view.dl_tensor = dl_tensor 

849 view.metadata = capsule 

850 view.ptr = <intptr_t>(dl_tensor.data) + <intptr_t>(dl_tensor.byte_offset) 

851 view.readonly = is_readonly 

852 view.exporting_obj = exporting_obj 

853 if dl_tensor.device.device_type == _kDLCPU: 

854 view.device_id = -1 

855 view.is_device_accessible = False 

856 elif dl_tensor.device.device_type in (_kDLCUDA, _kDLCUDAHost, _kDLCUDAManaged): 

857 view.device_id = dl_tensor.device.device_id 

858 view.is_device_accessible = True 

859 else: 

860 raise BufferError("device not supported") 

861 return view 

862  

863  

864cdef int _smv_managed_tensor_allocator( 

865 DLTensor* prototype, 

866 DLManagedTensorVersioned** out, 

867 void* error_ctx, 

868 void (*SetError)(void* error_ctx, const char* kind, const char* message) noexcept, 

869) noexcept with gil: 

870 if out != NULL: 

871 out[0] = NULL 

872 if SetError != NULL: 

873 SetError(error_ctx, b"NotImplementedError", b"managed_tensor_allocator is not supported by StridedMemoryView") 

874 cpython.PyErr_SetString(NotImplementedError, b"managed_tensor_allocator is not supported by StridedMemoryView") 

875 return -1 

876  

877  

878cdef int _smv_managed_tensor_from_py_object_no_sync( 

879 void* py_object, 

880 DLManagedTensorVersioned** out, 

881) noexcept with gil: 

882 cdef DLManagedTensorVersioned* dlm_tensor_ver = NULL 

883 if out == NULL: 

884 cpython.PyErr_SetString(RuntimeError, b"out cannot be NULL") 

885 return -1 

886 out[0] = NULL 

887 cdef object obj = <object>py_object 

888 if not isinstance(obj, StridedMemoryView): 

889 cpython.PyErr_SetString(TypeError, b"py_object must be a StridedMemoryView") 

890 return -1 

891 try: 

892 dlm_tensor_ver = _smv_allocate_dlm_tensor_versioned() 

893 _smv_fill_managed_tensor_versioned(dlm_tensor_ver, <StridedMemoryView>obj) 

894 except Exception: 

895 _smv_versioned_deleter(dlm_tensor_ver) 

896 return -1 

897 out[0] = dlm_tensor_ver 

898 return 0 

899  

900  

901cdef int _smv_managed_tensor_to_py_object_no_sync( 

902 DLManagedTensorVersioned* tensor, 

903 void** out_py_object, 

904) noexcept with gil: 

905 cdef object capsule 

906 cdef object py_view 

907 if out_py_object == NULL: 

908 cpython.PyErr_SetString(RuntimeError, b"out_py_object cannot be NULL") 

909 return -1 

910 out_py_object[0] = NULL 

911 if tensor == NULL: 

912 cpython.PyErr_SetString(RuntimeError, b"tensor cannot be NULL") 

913 return -1 

914 try: 

915 capsule = cpython.PyCapsule_New( 

916 <void*>tensor, 

917 DLPACK_VERSIONED_TENSOR_UNUSED_NAME, 

918 _smv_pycapsule_deleter, 

919 ) 

920 py_view = _smv_from_dlpack_capsule(capsule, capsule) 

921 cpython.Py_INCREF(py_view) 

922 out_py_object[0] = <void*>py_view 

923 except Exception: 

924 return -1 

925 return 0 

926  

927  

928cdef int _smv_dltensor_from_py_object_no_sync( 

929 void* py_object, 

930 DLTensor* out, 

931) noexcept with gil: 

932 if out == NULL: 

933 cpython.PyErr_SetString(RuntimeError, b"out cannot be NULL") 

934 return -1 

935 cdef object obj = <object>py_object 

936 if not isinstance(obj, StridedMemoryView): 

937 cpython.PyErr_SetString(TypeError, b"py_object must be a StridedMemoryView") 

938 return -1 

939 try: 

940 _smv_setup_dltensor_borrowed(out, <StridedMemoryView>obj) 

941 except Exception: 

942 return -1 

943 return 0 

944  

945  

946cdef int _smv_current_work_stream( 

947 _DLDeviceType device_type, 

948 int32_t device_id, 

949 void** out_current_stream, 

950) noexcept with gil: 

951 if out_current_stream == NULL: 

952 cpython.PyErr_SetString(RuntimeError, b"out_current_stream cannot be NULL") 

953 return -1 

954 # cuda.core has no global/current stream state today. 

955 out_current_stream[0] = NULL 

956 return 0 

957  

958  

959cdef void _init_smv_dlpack_exchange_api(): 

960 global _SMV_DLPACK_EXCHANGE_API_INITED 

961 if _SMV_DLPACK_EXCHANGE_API_INITED: 

962 return 

963 _SMV_DLPACK_EXCHANGE_API.header.version.major = DLPACK_MAJOR_VERSION 

964 _SMV_DLPACK_EXCHANGE_API.header.version.minor = DLPACK_MINOR_VERSION 

965 _SMV_DLPACK_EXCHANGE_API.header.prev_api = NULL 

966 _SMV_DLPACK_EXCHANGE_API.managed_tensor_allocator = _smv_managed_tensor_allocator 

967 _SMV_DLPACK_EXCHANGE_API.managed_tensor_from_py_object_no_sync = _smv_managed_tensor_from_py_object_no_sync 

968 _SMV_DLPACK_EXCHANGE_API.managed_tensor_to_py_object_no_sync = _smv_managed_tensor_to_py_object_no_sync 

969 _SMV_DLPACK_EXCHANGE_API.dltensor_from_py_object_no_sync = _smv_dltensor_from_py_object_no_sync 

970 _SMV_DLPACK_EXCHANGE_API.current_work_stream = _smv_current_work_stream 

971 _SMV_DLPACK_EXCHANGE_API_INITED = True 

972  

973  

974_init_smv_dlpack_exchange_api() 

975# cdef classes are immutable types in Cython 3, so inject these attributes 

976# directly into the type dict. 

977(<dict>(<PyTypeObject*>StridedMemoryView).tp_dict)["__dlpack_c_exchange_api__"] = _SMV_DLPACK_EXCHANGE_API_CAPSULE 

978(<dict>(<PyTypeObject*>StridedMemoryView).tp_dict)["__c_dlpack_exchange_api__"] = _SMV_DLPACK_EXCHANGE_API_CAPSULE 

979PyType_Modified(<PyTypeObject*>StridedMemoryView) 

980  

981  

982cdef str get_simple_repr(obj): 

983 # TODO: better handling in np.dtype objects 

984 cdef object obj_class 

985 cdef str obj_repr 

986 if isinstance(obj, type): 1m

987 obj_class = obj 

988 else: 

989 obj_class = obj.__class__ 1m

990 if obj_class.__module__ in (None, "builtins"): 1m

991 obj_repr = obj_class.__name__ 

992 else: 

993 obj_repr = f"{obj_class.__module__}.{obj_class.__name__}" 1m

994 return obj_repr 1m

995  

996  

997  

998cdef bint check_has_dlpack(obj) except*: 

999 cdef bint has_dlpack 

1000 if hasattr(obj, "__dlpack__") and hasattr(obj, "__dlpack_device__"): 2E F G Y t n o p q r u v w x y z A B C D Hbc d b W X a m V l

1001 has_dlpack = True 1EFGYtnopqruvwxyzABCDcdbWXamVl

1002 elif hasattr(obj, "__cuda_array_interface__"): 2Hb

1003 has_dlpack = False 

1004 else: 

1005 raise BufferError( 2Hb

1006 "the input object does not support any data exchange protocol") 

1007 return has_dlpack 1EFGYtnopqruvwxyzABCDcdbWXamVl

1008  

1009  

1010cdef class _StridedMemoryViewProxy: 

1011 cdef readonly: 

1012 object obj 

1013 bint has_dlpack 

1014  

1015 def __init__(self, obj: object) -> None: 

1016 self.obj = obj 1nopqr

1017 self.has_dlpack = check_has_dlpack(obj) 1nopqr

1018  

1019 cpdef StridedMemoryView view(self, stream_ptr=None): 

1020 if self.has_dlpack: 1nopqr

1021 return StridedMemoryView.from_dlpack(self.obj, stream_ptr) 1nopqr

1022 else: 

1023 return StridedMemoryView.from_cuda_array_interface(self.obj, stream_ptr) 

1024  

1025  

1026cdef StridedMemoryView view_as_dlpack(obj, stream_ptr, view=None): 

1027 cdef int dldevice, device_id 

1028 cdef bint is_device_accessible, is_readonly 

1029 is_device_accessible = False 1EFGYtnopqruvwxyzABCDcdbWXaHIJKLMNOPQRSTUmVlfghijk

1030 dldevice, device_id = obj.__dlpack_device__() 1EFGYtnopqruvwxyzABCDcdbWXaHIJKLMNOPQRSTUmVlfghijk

1031 if dldevice == _kDLCPU: 1EFGYtnopqruvwxyzABCDcdbWXaHIJKLMNOPQRSTUmVlfghijk

1032 assert device_id == 0 1GYtnopqruvwxyzABCDcdbWXaHIJKLMNOPQRSTUmVlfghijk

1033 device_id = -1 1GYtnopqruvwxyzABCDcdbWXaHIJKLMNOPQRSTUmVlfghijk

1034 if stream_ptr is None: 1GYtnopqruvwxyzABCDcdbWXaHIJKLMNOPQRSTUmVlfghijk

1035 raise BufferError("stream=None is ambiguous with view()") 

1036 elif stream_ptr == -1: 1GYtnopqruvwxyzABCDcdbWXaHIJKLMNOPQRSTUmVlfghijk

1037 stream_ptr = None 1GYtnopqruvwxyzABCDcdbWXaHIJKLMNOPQRSTUmVlfghijk

1038 elif dldevice == _kDLCUDA: 

1039 assert device_id >= 0 

1040 is_device_accessible = True 

1041 # no need to check other stream values, it's a pass-through 

1042 if stream_ptr is None: 

1043 raise BufferError("stream=None is ambiguous with view()") 

1044 elif dldevice in (_kDLCUDAHost, _kDLCUDAManaged): 

1045 is_device_accessible = True 1EF

1046 # just do a pass-through without any checks, as pinned/managed memory can be 

1047 # accessed on both host and device 

1048 else: 

1049 raise BufferError("device not supported") 

1050  

1051 cdef object capsule 

1052 try: 1EFGYtnopqruvwxyzABCDcdbWXaHIJKLMNOPQRSTUmVlfghijk

1053 capsule = obj.__dlpack__( 1EFGYtnopqruvwxyzABCDcdbWXaHIJKLMNOPQRSTUmVlfghijk

1054 stream=int(stream_ptr) if stream_ptr else None, 1EFGYtnopqruvwxyzABCDcdbWXaHIJKLMNOPQRSTUmVlfghijk

1055 max_version=(DLPACK_MAJOR_VERSION, DLPACK_MINOR_VERSION)) 1EFGYtnopqruvwxyzABCDcdbWXaHIJKLMNOPQRSTUmVlfghijk

1056 except TypeError: 

1057 capsule = obj.__dlpack__( 

1058 stream=int(stream_ptr) if stream_ptr else None) 

1059  

1060 cdef void* data = NULL 1EFGYtnopqruvwxyzABCDcdbWXaHIJKLMNOPQRSTUmVlfghijk

1061 cdef DLTensor* dl_tensor 

1062 cdef DLManagedTensorVersioned* dlm_tensor_ver 

1063 cdef DLManagedTensor* dlm_tensor 

1064 cdef const char *used_name 

1065 if cpython.PyCapsule_IsValid( 1EFGYtnopqruvwxyzABCDcdbWXaHIJKLMNOPQRSTUmVlfghijk

1066 capsule, DLPACK_VERSIONED_TENSOR_UNUSED_NAME): 

1067 data = cpython.PyCapsule_GetPointer( 1EFGYtnopqruvwxyzABCDcdbWXaHIJKLMNOPQRSTUmVlfghijk

1068 capsule, DLPACK_VERSIONED_TENSOR_UNUSED_NAME) 

1069 dlm_tensor_ver = <DLManagedTensorVersioned*>data 1EFGYtnopqruvwxyzABCDcdbWXaHIJKLMNOPQRSTUmVlfghijk

1070 dl_tensor = &dlm_tensor_ver.dl_tensor 1EFGYtnopqruvwxyzABCDcdbWXaHIJKLMNOPQRSTUmVlfghijk

1071 is_readonly = bool((dlm_tensor_ver.flags & DLPACK_FLAG_BITMASK_READ_ONLY) != 0) 1EFGYtnopqruvwxyzABCDcdbWXaHIJKLMNOPQRSTUmVlfghijk

1072 used_name = DLPACK_VERSIONED_TENSOR_USED_NAME 1EFGYtnopqruvwxyzABCDcdbWXaHIJKLMNOPQRSTUmVlfghijk

1073 elif cpython.PyCapsule_IsValid( 

1074 capsule, DLPACK_TENSOR_UNUSED_NAME): 

1075 data = cpython.PyCapsule_GetPointer( 

1076 capsule, DLPACK_TENSOR_UNUSED_NAME) 

1077 dlm_tensor = <DLManagedTensor*>data 

1078 dl_tensor = &dlm_tensor.dl_tensor 

1079 is_readonly = False 

1080 used_name = DLPACK_TENSOR_USED_NAME 

1081 else: 

1082 assert False 

1083  

1084 cpython.PyCapsule_SetName(capsule, used_name) 1EFGYtnopqruvwxyzABCDcdbWXaHIJKLMNOPQRSTUmVlfghijk

1085  

1086 cdef StridedMemoryView buf = StridedMemoryView() if view is None else view 1EFGYtnopqruvwxyzABCDcdbWXaHIJKLMNOPQRSTUmVlfghijk

1087 buf.dl_tensor = dl_tensor 1EFGYtnopqruvwxyzABCDcdbWXaHIJKLMNOPQRSTUmVlfghijk

1088 buf.metadata = capsule 1EFGYtnopqruvwxyzABCDcdbWXaHIJKLMNOPQRSTUmVlfghijk

1089 buf.ptr = <intptr_t>(dl_tensor.data) 1EFGYtnopqruvwxyzABCDcdbWXaHIJKLMNOPQRSTUmVlfghijk

1090 buf.device_id = device_id 1EFGYtnopqruvwxyzABCDcdbWXaHIJKLMNOPQRSTUmVlfghijk

1091 buf.is_device_accessible = is_device_accessible 1EFGYtnopqruvwxyzABCDcdbWXaHIJKLMNOPQRSTUmVlfghijk

1092 buf.readonly = is_readonly 1EFGYtnopqruvwxyzABCDcdbWXaHIJKLMNOPQRSTUmVlfghijk

1093 buf.exporting_obj = obj 1EFGYtnopqruvwxyzABCDcdbWXaHIJKLMNOPQRSTUmVlfghijk

1094  

1095 return buf 1EFGYtnopqruvwxyzABCDcdbWXaHIJKLMNOPQRSTUmVlfghijk

1096  

1097  

1098@functools.lru_cache 

1099def _typestr2dtype(str typestr) -> numpy.dtype: 

1100 return numpy.dtype(typestr) 1Z0123456789

1101  

1102  

1103@functools.lru_cache 

1104def _typestr2itemsize(str typestr) -> int: 

1105 return _typestr2dtype(typestr).itemsize 1Z0123456789

1106  

1107  

1108cdef object dtype_dlpack_to_numpy(DLDataType* dtype): 

1109 cdef int bits = dtype.bits 1aHIJKLMNOPQRSTUmfghijk

1110 if dtype.lanes != 1: 1aHIJKLMNOPQRSTUmfghijk

1111 # TODO: return a NumPy structured dtype? 

1112 raise NotImplementedError( 

1113 f'vector dtypes (lanes={dtype.lanes}) is not supported') 

1114 if dtype.code == kDLUInt: 1aHIJKLMNOPQRSTUmfghijk

1115 if bits == 8: 1RSTU

1116 np_dtype = numpy.uint8 1R

1117 elif bits == 16: 

1118 np_dtype = numpy.uint16 1S

1119 elif bits == 32: 

1120 np_dtype = numpy.uint32 1T

1121 elif bits == 64: 

1122 np_dtype = numpy.uint64 1U

1123 else: 

1124 raise TypeError('uint{} is not supported.'.format(bits)) 

1125 elif dtype.code == kDLInt: 

1126 if bits == 8: 1aNOPQmfghijk

1127 np_dtype = numpy.int8 1N

1128 elif bits == 16: 

1129 np_dtype = numpy.int16 1O

1130 elif bits == 32: 

1131 np_dtype = numpy.int32 1aPmfghijk

1132 elif bits == 64: 

1133 np_dtype = numpy.int64 1Q

1134 else: 

1135 raise TypeError('int{} is not supported.'.format(bits)) 

1136 elif dtype.code == kDLFloat: 

1137 if bits == 16: 1KLM

1138 np_dtype = numpy.float16 1K

1139 elif bits == 32: 

1140 np_dtype = numpy.float32 1L

1141 elif bits == 64: 

1142 np_dtype = numpy.float64 1M

1143 else: 

1144 raise TypeError('float{} is not supported.'.format(bits)) 

1145 elif dtype.code == kDLComplex: 

1146 # TODO(leofang): support complex32 

1147 if bits == 64: 1IJ

1148 np_dtype = numpy.complex64 1I

1149 elif bits == 128: 

1150 np_dtype = numpy.complex128 1J

1151 else: 

1152 raise TypeError('complex{} is not supported.'.format(bits)) 

1153 elif dtype.code == kDLBool: 

1154 if bits == 8: 1H

1155 np_dtype = numpy.bool_ 1H

1156 else: 

1157 raise TypeError(f'{bits}-bit bool is not supported') 

1158 elif dtype.code == kDLBfloat: 

1159 if bfloat16 is not None: 

1160 np_dtype = numpy.dtype("bfloat16") 

1161 else: 

1162 raise NotImplementedError( 

1163 'Support for bfloat16 within cuda-core requires `ml_dtypes`' 

1164 'to be installed.' 

1165 ) 

1166 else: 

1167 raise TypeError('Unsupported dtype. dtype code: {}'.format(dtype.code)) 

1168  

1169 # We want the dtype object not just the type object 

1170 return numpy.dtype(np_dtype) 1aHIJKLMNOPQRSTUmfghijk

1171  

1172  

1173cpdef StridedMemoryView view_as_cai(obj, stream_ptr, view=None): 

1174 cdef dict cai_data = obj.__cuda_array_interface__ 2IbGbKb; wb9

1175 if cai_data["version"] < 3: 2IbGbKb; wb9

1176 raise BufferError("only CUDA Array Interface v3 or above is supported") 2Kb

1177 if cai_data.get("mask") is not None: 2IbGb; wb9

1178 raise BufferError("mask is not supported") 2Ib

1179 if stream_ptr is None: 2Gb; wb9

1180 raise BufferError("stream=None is ambiguous with view()") 2Gb

1181  

1182 cdef StridedMemoryView buf = StridedMemoryView() if view is None else view 2; wb9

1183 buf.exporting_obj = obj 2; wb9

1184 buf.metadata = cai_data 2; wb9

1185 buf.dl_tensor = NULL 2; wb9

1186 # Validate shape/strides/typestr eagerly so constructor paths fail fast. 

1187 buf.get_layout() 2; wb9

1188 buf.ptr, buf.readonly = cai_data["data"] 1;9

1189 buf.is_device_accessible = True 1;9

1190 if buf.ptr != 0: 1;9

1191 buf.device_id = handle_return( 

1192 driver.cuPointerGetAttribute( 

1193 driver.CUpointer_attribute.CU_POINTER_ATTRIBUTE_DEVICE_ORDINAL, 

1194 buf.ptr)) 

1195 else: 

1196 buf.device_id = handle_return(driver.cuCtxGetDevice()) 1;9

1197  

1198 cdef intptr_t producer_s, consumer_s 

1199 cdef EventHandle h_event 

1200 stream_ptr = int(stream_ptr) 1;9

1201 if stream_ptr != -1: 1;9

1202 stream = cai_data.get("stream") 

1203 if stream is not None: 

1204 producer_s = <intptr_t>(stream) 

1205 consumer_s = <intptr_t>(stream_ptr) 

1206 assert producer_s > 0 

1207 # establish stream order 

1208 if producer_s != consumer_s: 

1209 with nogil: 

1210 h_event = create_event_handle_noctx(cydriver.CUevent_flags.CU_EVENT_DISABLE_TIMING) 

1211 HANDLE_RETURN(cydriver.cuEventRecord( 

1212 as_cu(h_event), <cydriver.CUstream>producer_s)) 

1213 HANDLE_RETURN(cydriver.cuStreamWaitEvent( 

1214 <cydriver.CUstream>consumer_s, as_cu(h_event), 0)) 

1215 elif _is_torch_tensor(obj): 

1216 # PyTorch's __cuda_array_interface__ reports version 2 and 

1217 # omits the "stream" field, so the standard CAI sync path 

1218 # above is a no-op for torch tensors. This is unsafe: the 

1219 # consumer has no guarantee that the producer's work is 

1220 # visible. We fix this by querying PyTorch's current CUDA 

1221 # stream via the AOTI stable C ABI and performing the same 

1222 # event-based stream ordering. 

1223 _get_tensor_bridge().sync_torch_stream( 

1224 buf.device_id, <intptr_t>(stream_ptr)) 

1225  

1226 return buf 1;9

1227  

1228  

1229cpdef StridedMemoryView view_as_array_interface(obj, view=None): 

1230 cdef dict data = obj.__array_interface__ 2JbLbZ 0 ! 1 # 2 $ 3 4 5 6 % ' ( 7 ) * + , - . / : 8 yb

1231 if data["version"] < 3: 2JbLbZ 0 ! 1 # 2 $ 3 4 5 6 % ' ( 7 ) * + , - . / : 8 yb

1232 raise BufferError("only NumPy Array Interface v3 or above is supported") 2Lb

1233 if data.get("mask") is not None: 2JbZ 0 ! 1 # 2 $ 3 4 5 6 % ' ( 7 ) * + , - . / : 8 yb

1234 raise BufferError("mask is not supported") 2Jb

1235  

1236 cdef StridedMemoryView buf = StridedMemoryView() if view is None else view 2Z 0 ! 1 # 2 $ 3 4 5 6 % ' ( 7 ) * + , - . / : 8 yb

1237 buf.exporting_obj = obj 2Z 0 ! 1 # 2 $ 3 4 5 6 % ' ( 7 ) * + , - . / : 8 yb

1238 buf.metadata = data 2Z 0 ! 1 # 2 $ 3 4 5 6 % ' ( 7 ) * + , - . / : 8 yb

1239 buf.dl_tensor = NULL 2Z 0 ! 1 # 2 $ 3 4 5 6 % ' ( 7 ) * + , - . / : 8 yb

1240 # Validate shape/strides/typestr eagerly so constructor paths fail fast. 

1241 buf.get_layout() 2Z 0 ! 1 # 2 $ 3 4 5 6 % ' ( 7 ) * + , - . / : 8 yb

1242 buf.ptr, buf.readonly = data["data"] 1Z0!1#2$3456%'(7)*+,-./:8

1243 buf.is_device_accessible = False 1Z0!1#2$3456%'(7)*+,-./:8

1244 buf.device_id = handle_return(driver.cuCtxGetDevice()) 1Z0!1#2$3456%'(7)*+,-./:8

1245 return buf 1Z0!1#2$3456%'(7)*+,-./:8

1246  

1247  

1248def args_viewable_as_strided_memory(arg_indices: tuple[int, ...]) -> Callable[[Callable[..., Any]], Callable[..., Any]]: 

1249 """ 

1250 Decorator to create proxy objects to :obj:`StridedMemoryView` for the 

1251 specified positional arguments. 

1252  

1253 This allows array/tensor attributes to be accessed inside the function 

1254 implementation, while keeping the function body array-library-agnostic (if 

1255 desired). 

1256  

1257 Inside the decorated function, the specified arguments become instances 

1258 of an (undocumented) proxy type, regardless of its original source. A 

1259 :obj:`StridedMemoryView` instance can be obtained by passing the (consumer) 

1260 stream pointer (as a Python `int`) to the proxies's ``view()`` method. For 

1261 example: 

1262  

1263 .. code-block:: python 

1264  

1265 @args_viewable_as_strided_memory((1,)) 

1266 def my_func(arg0, arg1, arg2, stream: Stream): 

1267 # arg1 can be any object supporting DLPack or CUDA Array Interface 

1268 view = arg1.view(stream.handle) 

1269 assert isinstance(view, StridedMemoryView) 

1270 ... 

1271  

1272 Parameters 

1273 ---------- 

1274 arg_indices : tuple 

1275 The indices of the target positional arguments. 

1276 """ 

1277 def wrapped_func_with_indices(func: "Callable") -> "Callable": 1nopqr

1278 @functools.wraps(func) 1nopqr

1279 def wrapped_func(*args, **kwargs) -> object: 

1280 args = list(args) 1nopqr

1281 cdef int idx 

1282 for idx in arg_indices: 1nopqr

1283 args[idx] = _StridedMemoryViewProxy(args[idx]) 1nopqr

1284 return func(*args, **kwargs) 1nopqr

1285 return wrapped_func 1nopqr

1286 return wrapped_func_with_indices 1nopqr

1287  

1288  

1289cdef inline _StridedLayout layout_from_dlpack(DLTensor* dl_tensor): 

1290 cdef _StridedLayout layout = _StridedLayout.__new__(_StridedLayout) 1tnopqruvwxyzABCDcdbamlfghijk

1291 cdef int nbits = dl_tensor.dtype.bits * dl_tensor.dtype.lanes 1tnopqruvwxyzABCDcdbamlfghijk

1292 cdef int itemsize = nbits >> 3 1tnopqruvwxyzABCDcdbamlfghijk

1293 if (itemsize << 3) != nbits: 1tnopqruvwxyzABCDcdbamlfghijk

1294 raise ValueError("dl_tensor.dtype.bits must be a multiple of 8") 

1295 layout.init_from_ptr(dl_tensor.ndim, dl_tensor.shape, dl_tensor.strides, itemsize) 1tnopqruvwxyzABCDcdbamlfghijk

1296 return layout 1tnopqruvwxyzABCDcdbamlfghijk

1297  

1298  

1299cdef _StridedLayout layout_from_cai(object metadata): 

1300 cdef _StridedLayout layout = _StridedLayout.__new__(_StridedLayout) 2Z 0 ! 1 # 2 $ 3 4 5 6 % ' ( 7 ) * + , - . / : 8 yb; wb9

1301 cdef object shape = metadata["shape"] 2Z 0 ! 1 # 2 $ 3 4 5 6 % ' ( 7 ) * + , - . / : 8 yb; wb9

1302 cdef object strides = metadata.get("strides") 2Z 0 ! 1 # 2 $ 3 4 5 6 % ' ( 7 ) * + , - . / : 8 yb; wb9

1303 cdef int itemsize = _typestr2itemsize(metadata["typestr"]) 2Z 0 ! 1 # 2 $ 3 4 5 6 % ' ( 7 ) * + , - . / : 8 yb; wb9

1304 layout.init_from_tuple(shape, strides, itemsize, True) 2Z 0 ! 1 # 2 $ 3 4 5 6 % ' ( 7 ) * + , - . / : 8 yb; wb9

1305 return layout 1Z0!1#2$3456%'(7)*+,-./:8;9

1306  

1307  

1308cdef inline intptr_t get_data_ptr(object buffer, _StridedLayout layout) except? 0: 

1309 return <intptr_t>(int(buffer.handle)) + layout.get_slice_offset_in_bytes() 2c d b @ [ ] ^ _ ` { | } ~ abbbcbdbebfbgbhbibjbkblbmbnbobpbqbrbsbtbubvb= ? xbs l f g h i j k

1310  

1311  

1312cdef inline int view_buffer_strided( 

1313 StridedMemoryView view, 

1314 object buffer, 

1315 _StridedLayout layout, 

1316 object dtype, 

1317 bint is_readonly, 

1318) except -1: 

1319 if dtype is not None: 2c d b @ [ ] ^ _ ` { | } ~ abbbcbdbebfbgbhbibjbkblbmbnbobpbqbrbsbtbubvbBb= ? zbxbs l f g h i j k

1320 dtype = numpy.dtype(dtype) 2c d b @ [ ] ^ _ ` { | } ~ abbbcbdbebfbgbhbibjbkblbmbnbobpbqbrbsbtbubvbBb= ? zbxbl f g h i j k

1321 if dtype.itemsize != layout.itemsize: 2c d b @ [ ] ^ _ ` { | } ~ abbbcbdbebfbgbhbibjbkblbmbnbobpbqbrbsbtbubvbBb= ? zbxbl f g h i j k

1322 raise ValueError( 

1323 f"The dtype's itemsize ({dtype.itemsize}) does not match the layout's " 

1324 f"itemsize ({layout.itemsize})." 

1325 ) 

1326 # Check the layout's offset range [min_offset, max_offset] fits 

1327 # within the [0, buffer.size - 1] range. 

1328 # The required_size_in_bytes fails if min_offset < 0. 

1329 # NB. For external memory, both positive and negative offsets can be valid, 

1330 # but for a proper check we'd need to know both size and data offset, 

1331 # while neither is reported by the packages. 

1332 cdef bint is_allocated = buffer.memory_resource is not None 2c d b @ [ ] ^ _ ` { | } ~ abbbcbdbebfbgbhbibjbkblbmbnbobpbqbrbsbtbubvbBb= ? zbxbs l f g h i j k

1333 if is_allocated and buffer.size < layout.get_required_size_in_bytes(): 2c d b @ [ ] ^ _ ` { | } ~ abbbcbdbebfbgbhbibjbkblbmbnbobpbqbrbsbtbubvbBb= ? zbxbs l f g h i j k

1334 raise ValueError( 2zb

1335 f"Buffer size is too small for the layout. " 2zb

1336 f"Expected at least {layout.get_required_size_in_bytes()} bytes, " 2zb

1337 f"got {buffer.size} bytes." 2zb

1338 ) 

1339 # set the public attributes 

1340 view.ptr = get_data_ptr(buffer, layout) 2c d b @ [ ] ^ _ ` { | } ~ abbbcbdbebfbgbhbibjbkblbmbnbobpbqbrbsbtbubvb= ? xbs l f g h i j k

1341 view.device_id = buffer.device_id 2c d b @ [ ] ^ _ ` { | } ~ abbbcbdbebfbgbhbibjbkblbmbnbobpbqbrbsbtbubvb= ? xbs l f g h i j k

1342 view.is_device_accessible = buffer.is_device_accessible 2c d b @ [ ] ^ _ ` { | } ~ abbbcbdbebfbgbhbibjbkblbmbnbobpbqbrbsbtbubvb= ? xbs l f g h i j k

1343 view.readonly = is_readonly 2c d b @ [ ] ^ _ ` { | } ~ abbbcbdbebfbgbhbibjbkblbmbnbobpbqbrbsbtbubvb= ? xbs l f g h i j k

1344 view.exporting_obj = view._buffer = buffer 2c d b @ [ ] ^ _ ` { | } ~ abbbcbdbebfbgbhbibjbkblbmbnbobpbqbrbsbtbubvb= ? xbs l f g h i j k

1345 # no dlpack/cai metadata 

1346 view.dl_tensor = NULL 2c d b @ [ ] ^ _ ` { | } ~ abbbcbdbebfbgbhbibjbkblbmbnbobpbqbrbsbtbubvb= ? xbs l f g h i j k

1347 view.metadata = None 2c d b @ [ ] ^ _ ` { | } ~ abbbcbdbebfbgbhbibjbkblbmbnbobpbqbrbsbtbubvb= ? xbs l f g h i j k

1348 # we get the layout from the caller 

1349 view._layout = layout 2c d b @ [ ] ^ _ ` { | } ~ abbbcbdbebfbgbhbibjbkblbmbnbobpbqbrbsbtbubvb= ? xbs l f g h i j k

1350 view._dtype = dtype 2c d b @ [ ] ^ _ ` { | } ~ abbbcbdbebfbgbhbibjbkblbmbnbobpbqbrbsbtbubvb= ? xbs l f g h i j k

1351 return 0 2c d b @ [ ] ^ _ ` { | } ~ abbbcbdbebfbgbhbibjbkblbmbnbobpbqbrbsbtbubvb= ? xbs l f g h i j k