Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion cuda_core/cuda/core/_memoryview.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -1102,7 +1102,7 @@ cdef StridedMemoryView view_as_dlpack(obj, stream_ptr, view=None):
cdef StridedMemoryView buf = StridedMemoryView() if view is None else view
buf.dl_tensor = dl_tensor
buf.metadata = capsule
buf.ptr = <intptr_t>(dl_tensor.data)
buf.ptr = <intptr_t>(dl_tensor.data) + <intptr_t>(dl_tensor.byte_offset)
buf.device_id = device_id
buf.is_device_accessible = is_device_accessible
buf.readonly = is_readonly
Expand Down
12 changes: 12 additions & 0 deletions cuda_core/docs/source/release/1.2.0-notes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,18 @@ Fixes and enhancements
Windows, both ``ctypes.CFUNCTYPE`` and ``ctypes.WINFUNCTYPE`` are accepted.
(`#2439 <https://github.com/NVIDIA/cuda-python/issues/2439>`__)

- :meth:`~utils.StridedMemoryView.from_dlpack`, and
:meth:`~utils.StridedMemoryView.from_any_interface` on a DLPack producer, now
add ``DLTensor.byte_offset`` to ``ptr``. DLPack places a tensor's first
element at ``data + byte_offset``, but ``ptr`` was taken from ``data`` alone,
so a producer that reported an allocation base in ``data`` and expressed a
slice as an offset yielded a view pointing ``byte_offset`` bytes before the
tensor, with no error raised. The offset was also lost permanently on a
round-trip, since the ``__dlpack__`` re-export writes ``ptr`` back out as
``data`` with ``byte_offset = 0``. The capsule-consuming path was already
correct.
(`#2592 <https://github.com/NVIDIA/cuda-python/issues/2592>`__)

Deprecation Notices
-------------------

Expand Down
40 changes: 40 additions & 0 deletions cuda_core/tests/test_utils_dlpack.py
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,46 @@ def __dlpack__(self, stream=None, max_version=None, **kwargs):
producer_deleter(dlm)


@pytest.mark.agent_authored(model="claude-opus-5")
@pytest.mark.parametrize(
"max_version, capsule_name, managed_cls",
[
pytest.param(None, b"dltensor", _DLManagedTensor, id="unversioned"),
pytest.param((1, 0), b"dltensor_versioned", _DLManagedTensorVersioned, id="versioned"),
],
)
def test_from_dlpack_honours_byte_offset(max_version, capsule_name, managed_cls):
"""DLPack puts a tensor's first element at ``data + byte_offset``, so a producer
may report the allocation base in ``data`` and express a slice as an offset.
``view.ptr`` must account for it, as the capsule-consuming path already does."""
src = np.arange(9, dtype=np.int32)
# View only the first 8 elements, so shifting by one element below stays inside
# the allocation and a regression fails an assertion instead of reading OOB.
base = StridedMemoryView.from_any_interface(src[:8], stream_ptr=-1)
capsule = base.__dlpack__(max_version=max_version)
dlm = ctypes.cast(_PyCapsule_GetPointer(capsule, capsule_name), ctypes.POINTER(managed_cls))
assert dlm.contents.dl_tensor.data == src.ctypes.data
assert dlm.contents.dl_tensor.byte_offset == 0

# Re-describe the same 8 elements as src[1:9]. byte_offset is the only field
# written: shape and strides share one producer-owned block, so leave them alone.
dlm.contents.dl_tensor.byte_offset = src.itemsize

class _Export:
def __dlpack_device__(self):
return base.__dlpack_device__()

def __dlpack__(self, stream=None, max_version=None, **kwargs):
if capsule_name == b"dltensor" and max_version is not None:
raise TypeError("force unversioned")
return capsule

view = StridedMemoryView.from_dlpack(_Export(), stream_ptr=-1)
assert view.ptr == src.ctypes.data + src.itemsize
assert view.shape == (8,)
assert np.array_equal(np.from_dlpack(view), src[1:])


_FN_FROM_PY = ctypes.PYFUNCTYPE(ctypes.c_int, ctypes.c_void_p, ctypes.POINTER(ctypes.c_void_p))
_FN_TO_PY = ctypes.PYFUNCTYPE(ctypes.c_int, ctypes.c_void_p, ctypes.POINTER(ctypes.c_void_p))
_FN_DLTENSOR_FROM_PY = ctypes.PYFUNCTYPE(ctypes.c_int, ctypes.c_void_p, ctypes.c_void_p)
Expand Down
Loading