# SPDX-FileCopyrightText: Copyright (c) 2021-2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: LicenseRef-NVIDIA-SOFTWARE-LICENSE

from cpython.buffer cimport PyObject_CheckBuffer, PyObject_GetBuffer, PyBuffer_Release, PyBUF_SIMPLE, PyBUF_ANY_CONTIGUOUS
from libc.stdlib cimport calloc, free
from libc.stdint cimport int32_t, uint32_t, int64_t, uint64_t
from libc.stddef cimport wchar_t
from libc.string cimport memcpy
import ctypes as _ctypes
cimport cuda.bindings.cydriver as cydriver
import cuda.bindings.driver as _driver
cimport cuda.bindings._lib.param_packer as param_packer
from cuda.bindings._internal._fast_enum import FastEnum as _FastEnum

cdef void* _callocWrapper(length, size):
    cdef void* out = calloc(length, size)
    if out is NULL:
        raise MemoryError('Failed to allocated length x size memory: {}x{}'.format(length, size))
    return out

cdef class _HelperKernelParams:
    supported_types = { # excluding void_p and None, which are handled specially
        _ctypes.c_bool,
        _ctypes.c_char,
        _ctypes.c_wchar,
        _ctypes.c_byte,
        _ctypes.c_ubyte,
        _ctypes.c_short,
        _ctypes.c_ushort,
        _ctypes.c_int,
        _ctypes.c_uint,
        _ctypes.c_long,
        _ctypes.c_ulong,
        _ctypes.c_longlong,
        _ctypes.c_ulonglong,
        _ctypes.c_size_t,
        _ctypes.c_float,
        _ctypes.c_double
    }

    max_param_size = max(_ctypes.sizeof(max(_HelperKernelParams.supported_types, key=lambda t:_ctypes.sizeof(t))), sizeof(void_ptr))

    def __cinit__(self, kernelParams):
        self._pyobj_acquired = False
        self._malloc_list_created = False
        if kernelParams is None:
            self._ckernelParams = NULL
        elif isinstance(kernelParams, (int)):
            # Easy run, user gave us an already configured void** address
            self._ckernelParams = <void**><void_ptr>kernelParams
        elif PyObject_CheckBuffer(kernelParams):
            # Easy run, get address from Python Buffer Protocol
            err_buffer = PyObject_GetBuffer(kernelParams, &self._pybuffer, PyBUF_SIMPLE | PyBUF_ANY_CONTIGUOUS)
            if err_buffer == -1:
                raise RuntimeError("Argument 'kernelParams' failed to retrieve buffer through Buffer Protocol")
            self._pyobj_acquired = True
            self._ckernelParams = <void**><void_ptr>self._pybuffer.buf
        elif isinstance(kernelParams, (tuple)) and len(kernelParams) == 2 and isinstance(kernelParams[0], (tuple)) and isinstance(kernelParams[1], (tuple)):
            # Hard run, construct and fill out contigues memory using provided kernel values and types based
            if len(kernelParams[0]) != len(kernelParams[1]):
                raise TypeError("Argument 'kernelParams' has tuples with different length")
            if len(kernelParams[0]) != 0:
                self._length = len(kernelParams[0])
                self._ckernelParams = <void**>_callocWrapper(len(kernelParams[0]), sizeof(void*))
                self._ckernelParamsData = <char*>_callocWrapper(len(kernelParams[0]), _HelperKernelParams.max_param_size)
                self._malloc_list_created = True

            idx = 0
            data_idx = 0
            for value, ctype in zip(kernelParams[0], kernelParams[1]):
                if ctype is None:
                    # special cases for None
                    if callable(getattr(value, 'getPtr', None)):
                        self._ckernelParams[idx] = <void*><void_ptr>value.getPtr()
                    elif isinstance(value, (_ctypes.Structure)):
                        self._ckernelParams[idx] = <void*><void_ptr>_ctypes.addressof(value)
                    elif isinstance(value, (_FastEnum)):
                        self._ckernelParams[idx] = &(self._ckernelParamsData[data_idx])
                        (<int*>self._ckernelParams[idx])[0] = value.value
                        data_idx += sizeof(int)
                    else:
                        raise TypeError("Provided argument is of type {} but expected Type {}, {} or CUDA Binding structure with getPtr() attribute".format(type(value), type(_ctypes.Structure), type(_ctypes.c_void_p)))
                elif ctype in _HelperKernelParams.supported_types:
                    self._ckernelParams[idx] = &(self._ckernelParamsData[data_idx])

                    # handle case where a float is passed as a double
                    if ctype == _ctypes.c_double and isinstance(value, _ctypes.c_float):
                        value = ctype(value.value)
                    if not isinstance(value, ctype): # make it a ctype
                        size = param_packer.feed(self._ckernelParams[idx], value, ctype)
                        if size == 0: # feed failed
                            value = ctype(value)
                            size = _ctypes.sizeof(ctype)
                            addr = <void*>(<void_ptr>_ctypes.addressof(value))
                            memcpy(self._ckernelParams[idx], addr, size)
                    else:
                        size = _ctypes.sizeof(ctype)
                        addr = <void*>(<void_ptr>_ctypes.addressof(value))
                        memcpy(self._ckernelParams[idx], addr, size)
                    data_idx += size
                elif ctype == _ctypes.c_void_p:
                    # special cases for void_p
                    if isinstance(value, (int, _ctypes.c_void_p)):
                        self._ckernelParams[idx] = &(self._ckernelParamsData[data_idx])
                        (<void_ptr*>self._ckernelParams[idx])[0] = value.value if isinstance(value, (_ctypes.c_void_p)) else value
                        data_idx += sizeof(void_ptr)
                    elif callable(getattr(value, 'getPtr', None)):
                        self._ckernelParams[idx] = &(self._ckernelParamsData[data_idx])
                        (<void_ptr*>self._ckernelParams[idx])[0] = value.getPtr()
                        data_idx += sizeof(void_ptr)
                    else:
                        raise TypeError("Provided argument is of type {} but expected Type {}, {} or CUDA Binding structure with getPtr() attribute".format(type(value), type(int), type(_ctypes.c_void_p)))
                else:
                    raise TypeError("Unsupported type: " + str(type(ctype)))
                idx += 1
        else:
            raise TypeError("Argument 'kernelParams' is not a valid type: tuple[tuple[Any, ...], tuple[Any, ...]] or PyObject implimenting Buffer Protocol or Int")

    def __dealloc__(self):
        if self._pyobj_acquired is True:
            PyBuffer_Release(&self._pybuffer)
        if self._malloc_list_created is True:
            free(self._ckernelParams)
            free(self._ckernelParamsData)

    @property
    def ckernelParams(self):
        return <void_ptr>self._ckernelParams

cdef class _HelperInputVoidPtr:
    def __cinit__(self, ptr):
        self._cptr = _helper_input_void_ptr(ptr, &self._helper)

    def __dealloc__(self):
        _helper_input_void_ptr_free(&self._helper)

    @property
    def cptr(self):
        return <void_ptr>self._cptr


cdef void * _helper_input_void_ptr(ptr, _HelperInputVoidPtrStruct *helper):
    helper[0]._pybuffer.buf = NULL
    try:
        return <void *><void_ptr>ptr
    except:
        if ptr is None:
            return NULL
        elif PyObject_CheckBuffer(ptr):
            # Easy run, get address from Python Buffer Protocol
            err_buffer = PyObject_GetBuffer(ptr, &helper[0]._pybuffer, PyBUF_SIMPLE | PyBUF_ANY_CONTIGUOUS)
            if err_buffer == -1:
                raise RuntimeError("Failed to retrieve buffer through Buffer Protocol")
            return <void*><void_ptr>(helper[0]._pybuffer.buf)
        else:
            raise TypeError("Provided argument is of type {} but expected Type {}, {} or object with Buffer Protocol".format(type(ptr), type(None), type(int)))


{{if 'CUmemPool_attribute_enum' in found_types}}

cdef class _HelperCUmemPool_attribute:
    def __cinit__(self, attr, init_value, is_getter=False):
        self._is_getter = is_getter
        self._attr = attr.value
        if self._attr in ({{if 'CU_MEMPOOL_ATTR_REUSE_FOLLOW_EVENT_DEPENDENCIES'}}cydriver.CUmemPool_attribute_enum.CU_MEMPOOL_ATTR_REUSE_FOLLOW_EVENT_DEPENDENCIES,{{endif}}
                          {{if 'CU_MEMPOOL_ATTR_REUSE_ALLOW_OPPORTUNISTIC'}}cydriver.CUmemPool_attribute_enum.CU_MEMPOOL_ATTR_REUSE_ALLOW_OPPORTUNISTIC,{{endif}}
                          {{if 'CU_MEMPOOL_ATTR_REUSE_ALLOW_INTERNAL_DEPENDENCIES'}}cydriver.CUmemPool_attribute_enum.CU_MEMPOOL_ATTR_REUSE_ALLOW_INTERNAL_DEPENDENCIES,{{endif}}):
            self._int_val = init_value
            self._cptr = <void*>&self._int_val
        elif self._attr in ({{if 'CU_MEMPOOL_ATTR_RELEASE_THRESHOLD'}}cydriver.CUmemPool_attribute_enum.CU_MEMPOOL_ATTR_RELEASE_THRESHOLD,{{endif}}
                            {{if 'CU_MEMPOOL_ATTR_RESERVED_MEM_CURRENT'}}cydriver.CUmemPool_attribute_enum.CU_MEMPOOL_ATTR_RESERVED_MEM_CURRENT,{{endif}}
                            {{if 'CU_MEMPOOL_ATTR_RESERVED_MEM_HIGH'}}cydriver.CUmemPool_attribute_enum.CU_MEMPOOL_ATTR_RESERVED_MEM_HIGH,{{endif}}
                            {{if 'CU_MEMPOOL_ATTR_USED_MEM_CURRENT'}}cydriver.CUmemPool_attribute_enum.CU_MEMPOOL_ATTR_USED_MEM_CURRENT,{{endif}}
                            {{if 'CU_MEMPOOL_ATTR_USED_MEM_HIGH'}}cydriver.CUmemPool_attribute_enum.CU_MEMPOOL_ATTR_USED_MEM_HIGH,{{endif}}):
            if self._is_getter:
                self._cuuint64_t_val = _driver.cuuint64_t()
                self._cptr = <void*><void_ptr>self._cuuint64_t_val.getPtr()
            else:
                self._cptr = <void*><void_ptr>init_value.getPtr()
        else:
            raise TypeError('Unsupported attribute: {}'.format(attr.name))

    def __dealloc__(self):
        pass

    @property
    def cptr(self):
        return <void_ptr>self._cptr

    def pyObj(self):
        assert(self._is_getter == True)
        if self._attr in ({{if 'CU_MEMPOOL_ATTR_REUSE_FOLLOW_EVENT_DEPENDENCIES'}}cydriver.CUmemPool_attribute_enum.CU_MEMPOOL_ATTR_REUSE_FOLLOW_EVENT_DEPENDENCIES,{{endif}}
                          {{if 'CU_MEMPOOL_ATTR_REUSE_ALLOW_OPPORTUNISTIC'}}cydriver.CUmemPool_attribute_enum.CU_MEMPOOL_ATTR_REUSE_ALLOW_OPPORTUNISTIC,{{endif}}
                          {{if 'CU_MEMPOOL_ATTR_REUSE_ALLOW_INTERNAL_DEPENDENCIES'}}cydriver.CUmemPool_attribute_enum.CU_MEMPOOL_ATTR_REUSE_ALLOW_INTERNAL_DEPENDENCIES,{{endif}}):
            return self._int_val
        elif self._attr in ({{if 'CU_MEMPOOL_ATTR_RELEASE_THRESHOLD'}}cydriver.CUmemPool_attribute_enum.CU_MEMPOOL_ATTR_RELEASE_THRESHOLD,{{endif}}
                            {{if 'CU_MEMPOOL_ATTR_RESERVED_MEM_CURRENT'}}cydriver.CUmemPool_attribute_enum.CU_MEMPOOL_ATTR_RESERVED_MEM_CURRENT,{{endif}}
                            {{if 'CU_MEMPOOL_ATTR_RESERVED_MEM_HIGH'}}cydriver.CUmemPool_attribute_enum.CU_MEMPOOL_ATTR_RESERVED_MEM_HIGH,{{endif}}
                            {{if 'CU_MEMPOOL_ATTR_USED_MEM_CURRENT'}}cydriver.CUmemPool_attribute_enum.CU_MEMPOOL_ATTR_USED_MEM_CURRENT,{{endif}}
                            {{if 'CU_MEMPOOL_ATTR_USED_MEM_HIGH'}}cydriver.CUmemPool_attribute_enum.CU_MEMPOOL_ATTR_USED_MEM_HIGH,{{endif}}):
            return self._cuuint64_t_val
        else:
            raise TypeError('Unsupported attribute value: {}'.format(self._attr))
{{endif}}
{{if 'CUmem_range_attribute_enum' in found_types}}

cdef class _HelperCUmem_range_attribute:
    def __cinit__(self, attr, data_size):
        self._data_size = data_size
        self._attr = attr.value
        if self._attr in ({{if 'CU_MEM_RANGE_ATTRIBUTE_READ_MOSTLY'}}cydriver.CUmem_range_attribute_enum.CU_MEM_RANGE_ATTRIBUTE_READ_MOSTLY,{{endif}}
                          {{if 'CU_MEM_RANGE_ATTRIBUTE_PREFERRED_LOCATION'}}cydriver.CUmem_range_attribute_enum.CU_MEM_RANGE_ATTRIBUTE_PREFERRED_LOCATION,{{endif}}
                          {{if 'CU_MEM_RANGE_ATTRIBUTE_LAST_PREFETCH_LOCATION'}}cydriver.CUmem_range_attribute_enum.CU_MEM_RANGE_ATTRIBUTE_LAST_PREFETCH_LOCATION,{{endif}}):
            self._cptr = <void*>&self._int_val
        elif self._attr in ({{if 'CU_MEM_RANGE_ATTRIBUTE_ACCESSED_BY'}}cydriver.CUmem_range_attribute_enum.CU_MEM_RANGE_ATTRIBUTE_ACCESSED_BY,{{endif}}):
            self._cptr = _callocWrapper(1, self._data_size)
            self._int_val_list = <int*>self._cptr
        else:
            raise TypeError('Unsupported attribute: {}'.format(attr.name))

    def __dealloc__(self):
        if self._attr in ({{if 'CU_MEM_RANGE_ATTRIBUTE_ACCESSED_BY'}}cydriver.CUmem_range_attribute_enum.CU_MEM_RANGE_ATTRIBUTE_ACCESSED_BY,{{endif}}):
            free(self._cptr)

    @property
    def cptr(self):
        return <void_ptr>self._cptr

    def pyObj(self):
        if self._attr in ({{if 'CU_MEM_RANGE_ATTRIBUTE_READ_MOSTLY'}}cydriver.CUmem_range_attribute_enum.CU_MEM_RANGE_ATTRIBUTE_READ_MOSTLY,{{endif}}
                          {{if 'CU_MEM_RANGE_ATTRIBUTE_PREFERRED_LOCATION'}}cydriver.CUmem_range_attribute_enum.CU_MEM_RANGE_ATTRIBUTE_PREFERRED_LOCATION,{{endif}}
                          {{if 'CU_MEM_RANGE_ATTRIBUTE_LAST_PREFETCH_LOCATION'}}cydriver.CUmem_range_attribute_enum.CU_MEM_RANGE_ATTRIBUTE_LAST_PREFETCH_LOCATION,{{endif}}):
            return self._int_val
        elif self._attr in ({{if 'CU_MEM_RANGE_ATTRIBUTE_ACCESSED_BY'}}cydriver.CUmem_range_attribute_enum.CU_MEM_RANGE_ATTRIBUTE_ACCESSED_BY,{{endif}}):
            return [self._int_val_list[idx] for idx in range(int(self._data_size/4))]
        else:
            raise TypeError('Unsupported attribute value: {}'.format(self._attr))
{{endif}}
{{if 'CUpointer_attribute_enum' in found_types}}

cdef class _HelperCUpointer_attribute:
    def __cinit__(self, attr, init_value, is_getter=False):
        self._is_getter = is_getter
        self._attr = attr.value
        if self._attr in ({{if 'CU_POINTER_ATTRIBUTE_CONTEXT'}}cydriver.CUpointer_attribute_enum.CU_POINTER_ATTRIBUTE_CONTEXT,{{endif}}):
            if self._is_getter:
                self._ctx = _driver.CUcontext()
                self._cptr = <void*><void_ptr>self._ctx.getPtr()
            else:
                self._cptr = <void*><void_ptr>init_value.getPtr()
        elif self._attr in ({{if 'CU_POINTER_ATTRIBUTE_MEMORY_TYPE'}}cydriver.CUpointer_attribute_enum.CU_POINTER_ATTRIBUTE_MEMORY_TYPE,{{endif}}
                            {{if 'CU_POINTER_ATTRIBUTE_ALLOWED_HANDLE_TYPES'}}cydriver.CUpointer_attribute_enum.CU_POINTER_ATTRIBUTE_ALLOWED_HANDLE_TYPES,{{endif}}
                            {{if 'CU_POINTER_ATTRIBUTE_IS_GPU_DIRECT_RDMA_CAPABLE'}}cydriver.CUpointer_attribute_enum.CU_POINTER_ATTRIBUTE_IS_GPU_DIRECT_RDMA_CAPABLE,{{endif}}
                            {{if 'CU_POINTER_ATTRIBUTE_ACCESS_FLAGS'}}cydriver.CUpointer_attribute_enum.CU_POINTER_ATTRIBUTE_ACCESS_FLAGS,{{endif}}):
            self._uint = init_value
            self._cptr = <void*>&self._uint
        elif self._attr in ({{if 'CU_POINTER_ATTRIBUTE_DEVICE_ORDINAL'}}cydriver.CUpointer_attribute_enum.CU_POINTER_ATTRIBUTE_DEVICE_ORDINAL,{{endif}}):
            self._int = init_value
            self._cptr = <void*>&self._int
        elif self._attr in ({{if 'CU_POINTER_ATTRIBUTE_DEVICE_POINTER'}}cydriver.CUpointer_attribute_enum.CU_POINTER_ATTRIBUTE_DEVICE_POINTER,{{endif}}
                            {{if 'CU_POINTER_ATTRIBUTE_RANGE_START_ADDR'}}cydriver.CUpointer_attribute_enum.CU_POINTER_ATTRIBUTE_RANGE_START_ADDR,{{endif}}):
            if self._is_getter:
                self._devptr = _driver.CUdeviceptr()
                self._cptr = <void*><void_ptr>self._devptr.getPtr()
            else:
                self._cptr = <void*><void_ptr>init_value.getPtr()
        elif self._attr in ({{if 'CU_POINTER_ATTRIBUTE_HOST_POINTER'}}cydriver.CUpointer_attribute_enum.CU_POINTER_ATTRIBUTE_HOST_POINTER,{{endif}}):
            self._void = <void**><void_ptr>init_value
            self._cptr = <void*>&self._void
        elif self._attr in ({{if 'CU_POINTER_ATTRIBUTE_P2P_TOKENS'}}cydriver.CUpointer_attribute_enum.CU_POINTER_ATTRIBUTE_P2P_TOKENS,{{endif}}):
            if self._is_getter:
                self._token = _driver.CUDA_POINTER_ATTRIBUTE_P2P_TOKENS()
                self._cptr = <void*><void_ptr>self._token.getPtr()
            else:
                self._cptr = <void*><void_ptr>init_value.getPtr()
        elif self._attr in ({{if 'CU_POINTER_ATTRIBUTE_SYNC_MEMOPS'}}cydriver.CUpointer_attribute_enum.CU_POINTER_ATTRIBUTE_SYNC_MEMOPS,{{endif}}
                            {{if 'CU_POINTER_ATTRIBUTE_IS_MANAGED'}}cydriver.CUpointer_attribute_enum.CU_POINTER_ATTRIBUTE_IS_MANAGED,{{endif}}
                            {{if 'CU_POINTER_ATTRIBUTE_IS_LEGACY_CUDA_IPC_CAPABLE'}}cydriver.CUpointer_attribute_enum.CU_POINTER_ATTRIBUTE_IS_LEGACY_CUDA_IPC_CAPABLE,{{endif}}
                            {{if 'CU_POINTER_ATTRIBUTE_MAPPED'}}cydriver.CUpointer_attribute_enum.CU_POINTER_ATTRIBUTE_MAPPED,{{endif}}):
            self._bool = init_value
            self._cptr = <void*>&self._bool
        elif self._attr in ({{if 'CU_POINTER_ATTRIBUTE_BUFFER_ID'}}cydriver.CUpointer_attribute_enum.CU_POINTER_ATTRIBUTE_BUFFER_ID,{{endif}}):
            self._ull = init_value
            self._cptr = <void*>&self._ull
        elif self._attr in ({{if 'CU_POINTER_ATTRIBUTE_RANGE_SIZE'}}cydriver.CUpointer_attribute_enum.CU_POINTER_ATTRIBUTE_RANGE_SIZE,{{endif}}):
            self._size = init_value
            self._cptr = <void*>&self._size
        elif self._attr in ({{if 'CU_POINTER_ATTRIBUTE_MEMPOOL_HANDLE'}}cydriver.CUpointer_attribute_enum.CU_POINTER_ATTRIBUTE_MEMPOOL_HANDLE,{{endif}}):
            if self._is_getter:
                self._mempool = _driver.CUmemoryPool()
                self._cptr = <void*><void_ptr>self._mempool.getPtr()
            else:
                self._cptr = <void*><void_ptr>init_value.getPtr()
        else:
            raise TypeError('Unsupported attribute: {}'.format(attr.name))

    def __dealloc__(self):
        pass

    @property
    def cptr(self):
        return <void_ptr>self._cptr

    def pyObj(self):
        assert(self._is_getter == True)
        if self._attr in ({{if 'CU_POINTER_ATTRIBUTE_CONTEXT'}}cydriver.CUpointer_attribute_enum.CU_POINTER_ATTRIBUTE_CONTEXT,{{endif}}):
            return self._ctx
        elif self._attr in ({{if 'CU_POINTER_ATTRIBUTE_MEMORY_TYPE'}}cydriver.CUpointer_attribute_enum.CU_POINTER_ATTRIBUTE_MEMORY_TYPE,{{endif}}
                            {{if 'CU_POINTER_ATTRIBUTE_DEVICE_ORDINAL'}}cydriver.CUpointer_attribute_enum.CU_POINTER_ATTRIBUTE_DEVICE_ORDINAL,{{endif}}
                            {{if 'CU_POINTER_ATTRIBUTE_ALLOWED_HANDLE_TYPES'}}cydriver.CUpointer_attribute_enum.CU_POINTER_ATTRIBUTE_ALLOWED_HANDLE_TYPES,{{endif}}
                            {{if 'CU_POINTER_ATTRIBUTE_IS_GPU_DIRECT_RDMA_CAPABLE'}}cydriver.CUpointer_attribute_enum.CU_POINTER_ATTRIBUTE_IS_GPU_DIRECT_RDMA_CAPABLE,{{endif}}
                            {{if 'CU_POINTER_ATTRIBUTE_ACCESS_FLAGS'}}cydriver.CUpointer_attribute_enum.CU_POINTER_ATTRIBUTE_ACCESS_FLAGS,{{endif}}):
            return self._uint
        elif self._attr in ({{if 'CU_POINTER_ATTRIBUTE_DEVICE_POINTER'}}cydriver.CUpointer_attribute_enum.CU_POINTER_ATTRIBUTE_DEVICE_POINTER,{{endif}}
                            {{if 'CU_POINTER_ATTRIBUTE_RANGE_START_ADDR'}}cydriver.CUpointer_attribute_enum.CU_POINTER_ATTRIBUTE_RANGE_START_ADDR,{{endif}}):
            return self._devptr
        elif self._attr in ({{if 'CU_POINTER_ATTRIBUTE_HOST_POINTER'}}cydriver.CUpointer_attribute_enum.CU_POINTER_ATTRIBUTE_HOST_POINTER,{{endif}}):
            return <void_ptr>self._void
        elif self._attr in ({{if 'CU_POINTER_ATTRIBUTE_P2P_TOKENS'}}cydriver.CUpointer_attribute_enum.CU_POINTER_ATTRIBUTE_P2P_TOKENS,{{endif}}):
            return self._token
        elif self._attr in ({{if 'CU_POINTER_ATTRIBUTE_SYNC_MEMOPS'}}cydriver.CUpointer_attribute_enum.CU_POINTER_ATTRIBUTE_SYNC_MEMOPS,{{endif}}
                            {{if 'CU_POINTER_ATTRIBUTE_IS_MANAGED'}}cydriver.CUpointer_attribute_enum.CU_POINTER_ATTRIBUTE_IS_MANAGED,{{endif}}
                            {{if 'CU_POINTER_ATTRIBUTE_IS_LEGACY_CUDA_IPC_CAPABLE'}}cydriver.CUpointer_attribute_enum.CU_POINTER_ATTRIBUTE_IS_LEGACY_CUDA_IPC_CAPABLE,{{endif}}
                            {{if 'CU_POINTER_ATTRIBUTE_MAPPED'}}cydriver.CUpointer_attribute_enum.CU_POINTER_ATTRIBUTE_MAPPED,{{endif}}):
            return self._bool
        elif self._attr in ({{if 'CU_POINTER_ATTRIBUTE_BUFFER_ID'}}cydriver.CUpointer_attribute_enum.CU_POINTER_ATTRIBUTE_BUFFER_ID,{{endif}}):
            return self._ull
        elif self._attr in ({{if 'CU_POINTER_ATTRIBUTE_RANGE_SIZE'}}cydriver.CUpointer_attribute_enum.CU_POINTER_ATTRIBUTE_RANGE_SIZE,{{endif}}):
            return self._size
        elif self._attr in ({{if 'CU_POINTER_ATTRIBUTE_MEMPOOL_HANDLE'}}cydriver.CUpointer_attribute_enum.CU_POINTER_ATTRIBUTE_MEMPOOL_HANDLE,{{endif}}):
            return self._mempool
        else:
            raise TypeError('Unsupported attribute value: {}'.format(self._attr))
{{endif}}
{{if 'CUgraphMem_attribute_enum' in found_types}}

cdef class _HelperCUgraphMem_attribute:
    def __cinit__(self, attr, init_value, is_getter=False):
        self._is_getter = is_getter
        self._attr = attr.value
        if self._attr in ({{if 'CU_GRAPH_MEM_ATTR_USED_MEM_CURRENT' in found_values}}cydriver.CUgraphMem_attribute_enum.CU_GRAPH_MEM_ATTR_USED_MEM_CURRENT,{{endif}}
                          {{if 'CU_GRAPH_MEM_ATTR_USED_MEM_HIGH' in found_values}}cydriver.CUgraphMem_attribute_enum.CU_GRAPH_MEM_ATTR_USED_MEM_HIGH,{{endif}}
                          {{if 'CU_GRAPH_MEM_ATTR_RESERVED_MEM_CURRENT' in found_values}}cydriver.CUgraphMem_attribute_enum.CU_GRAPH_MEM_ATTR_RESERVED_MEM_CURRENT,{{endif}}
                          {{if 'CU_GRAPH_MEM_ATTR_RESERVED_MEM_HIGH' in found_values}}cydriver.CUgraphMem_attribute_enum.CU_GRAPH_MEM_ATTR_RESERVED_MEM_HIGH,{{endif}}):
            if self._is_getter:
                self._cuuint64_t_val = _driver.cuuint64_t()
                self._cptr = <void*><void_ptr>self._cuuint64_t_val.getPtr()
            else:
                self._cptr = <void*><void_ptr>init_value.getPtr()
        else:
            raise TypeError('Unsupported attribute: {}'.format(attr.name))

    def __dealloc__(self):
        pass

    @property
    def cptr(self):
        return <void_ptr>self._cptr

    def pyObj(self):
        assert(self._is_getter == True)
        if self._attr in ({{if 'CU_GRAPH_MEM_ATTR_USED_MEM_CURRENT' in found_values}}cydriver.CUgraphMem_attribute_enum.CU_GRAPH_MEM_ATTR_USED_MEM_CURRENT,{{endif}}
                          {{if 'CU_GRAPH_MEM_ATTR_USED_MEM_HIGH' in found_values}}cydriver.CUgraphMem_attribute_enum.CU_GRAPH_MEM_ATTR_USED_MEM_HIGH,{{endif}}
                          {{if 'CU_GRAPH_MEM_ATTR_RESERVED_MEM_CURRENT' in found_values}}cydriver.CUgraphMem_attribute_enum.CU_GRAPH_MEM_ATTR_RESERVED_MEM_CURRENT,{{endif}}
                          {{if 'CU_GRAPH_MEM_ATTR_RESERVED_MEM_HIGH' in found_values}}cydriver.CUgraphMem_attribute_enum.CU_GRAPH_MEM_ATTR_RESERVED_MEM_HIGH,{{endif}}):
            return self._cuuint64_t_val
        else:
            raise TypeError('Unsupported attribute value: {}'.format(self._attr))
{{endif}}
{{if 'CUjit_option_enum' in found_types}}

cdef class _HelperCUjit_option:
    def __cinit__(self, attr, init_value):
        self._attr = attr.value
        if self._attr in ({{if 'CU_JIT_MAX_REGISTERS' in found_values}}cydriver.CUjit_option_enum.CU_JIT_MAX_REGISTERS,{{endif}}
                          {{if 'CU_JIT_THREADS_PER_BLOCK' in found_values}}cydriver.CUjit_option_enum.CU_JIT_THREADS_PER_BLOCK,{{endif}}
                          {{if 'CU_JIT_INFO_LOG_BUFFER_SIZE_BYTES' in found_values}}cydriver.CUjit_option_enum.CU_JIT_INFO_LOG_BUFFER_SIZE_BYTES,{{endif}}
                          {{if 'CU_JIT_ERROR_LOG_BUFFER_SIZE_BYTES' in found_values}}cydriver.CUjit_option_enum.CU_JIT_ERROR_LOG_BUFFER_SIZE_BYTES,{{endif}}
                          {{if 'CU_JIT_OPTIMIZATION_LEVEL' in found_values}}cydriver.CUjit_option_enum.CU_JIT_OPTIMIZATION_LEVEL,{{endif}}
                          {{if 'CU_JIT_GLOBAL_SYMBOL_COUNT' in found_values}}cydriver.CUjit_option_enum.CU_JIT_GLOBAL_SYMBOL_COUNT,{{endif}}
                          {{if 'CU_JIT_TARGET_FROM_CUCONTEXT' in found_values}}cydriver.CUjit_option_enum.CU_JIT_TARGET_FROM_CUCONTEXT,{{endif}}
                          {{if 'CU_JIT_REFERENCED_KERNEL_COUNT' in found_values}}cydriver.CUjit_option_enum.CU_JIT_REFERENCED_KERNEL_COUNT,{{endif}}
                          {{if 'CU_JIT_REFERENCED_VARIABLE_COUNT' in found_values}}cydriver.CUjit_option_enum.CU_JIT_REFERENCED_VARIABLE_COUNT,{{endif}}
                          {{if 'CU_JIT_MIN_CTA_PER_SM' in found_values}}cydriver.CUjit_option_enum.CU_JIT_MIN_CTA_PER_SM,{{endif}}):
            self._uint = init_value
            self._cptr = <void*><void_ptr>self._uint
        elif self._attr in ({{if 'CU_JIT_WALL_TIME' in found_values}}cydriver.CUjit_option_enum.CU_JIT_WALL_TIME,{{endif}}):
            self._float = init_value
            self._cptr = <void*><void_ptr>self._float
        elif self._attr in ({{if 'CU_JIT_INFO_LOG_BUFFER' in found_values}}cydriver.CUjit_option_enum.CU_JIT_INFO_LOG_BUFFER,{{endif}}
                            {{if 'CU_JIT_ERROR_LOG_BUFFER' in found_values}}cydriver.CUjit_option_enum.CU_JIT_ERROR_LOG_BUFFER{{endif}}):
            self._charstar = init_value
            self._cptr = <void*><void_ptr>self._charstar
        elif self._attr in ({{if 'CU_JIT_TARGET' in found_values}}cydriver.CUjit_option_enum.CU_JIT_TARGET,{{endif}}):
            self._target = init_value.value
            self._cptr = <void*><void_ptr>self._target
        elif self._attr in ({{if 'CU_JIT_FALLBACK_STRATEGY' in found_values}}cydriver.CUjit_option_enum.CU_JIT_FALLBACK_STRATEGY,{{endif}}):
            self._fallback = init_value.value
            self._cptr = <void*><void_ptr>self._fallback
        elif self._attr in ({{if 'CU_JIT_GENERATE_DEBUG_INFO' in found_values}}cydriver.CUjit_option_enum.CU_JIT_GENERATE_DEBUG_INFO,{{endif}}
                            {{if 'CU_JIT_LOG_VERBOSE' in found_values}}cydriver.CUjit_option_enum.CU_JIT_LOG_VERBOSE,{{endif}}
                            {{if 'CU_JIT_GENERATE_LINE_INFO' in found_values}}cydriver.CUjit_option_enum.CU_JIT_GENERATE_LINE_INFO,{{endif}}
                            {{if 'CU_JIT_LTO' in found_values}}cydriver.CUjit_option_enum.CU_JIT_LTO,{{endif}}
                            {{if 'CU_JIT_FTZ' in found_values}}cydriver.CUjit_option_enum.CU_JIT_FTZ,{{endif}}
                            {{if 'CU_JIT_PREC_DIV' in found_values}}cydriver.CUjit_option_enum.CU_JIT_PREC_DIV,{{endif}}
                            {{if 'CU_JIT_PREC_SQRT' in found_values}}cydriver.CUjit_option_enum.CU_JIT_PREC_SQRT,{{endif}}
                            {{if 'CU_JIT_FMA' in found_values}}cydriver.CUjit_option_enum.CU_JIT_FMA,{{endif}}
                            {{if 'CU_JIT_OPTIMIZE_UNUSED_DEVICE_VARIABLES' in found_values}}cydriver.CUjit_option_enum.CU_JIT_OPTIMIZE_UNUSED_DEVICE_VARIABLES,{{endif}}):
            self._int = init_value
            self._cptr = <void*><void_ptr>self._int
        elif self._attr in ({{if 'CU_JIT_CACHE_MODE' in found_values}}cydriver.CUjit_option_enum.CU_JIT_CACHE_MODE,{{endif}}):
            self._cacheMode = init_value.value
            self._cptr = <void*><void_ptr>self._cacheMode
        elif self._attr in ({{if 'CU_JIT_GLOBAL_SYMBOL_NAMES' in found_values}}cydriver.CUjit_option_enum.CU_JIT_GLOBAL_SYMBOL_NAMES,{{endif}}
                            {{if 'CU_JIT_REFERENCED_KERNEL_NAMES' in found_values}}cydriver.CUjit_option_enum.CU_JIT_REFERENCED_KERNEL_NAMES,{{endif}}
                            {{if 'CU_JIT_REFERENCED_VARIABLE_NAMES' in found_values}}cydriver.CUjit_option_enum.CU_JIT_REFERENCED_VARIABLE_NAMES,{{endif}}):
            self._charstarstar = init_value
            self._cptr = <void*>&self._charstarstar[0]
        elif self._attr in ({{if 'CU_JIT_GLOBAL_SYMBOL_ADDRESSES' in found_values}}cydriver.CUjit_option_enum.CU_JIT_GLOBAL_SYMBOL_ADDRESSES,{{endif}}):
            pylist = [_HelperInputVoidPtr(val) for val in init_value]
            self._voidstarstar = _InputVoidPtrPtrHelper(pylist)
            self._cptr = <void*><void_ptr>self._voidstarstar.cptr
        else:
            raise TypeError('Unsupported attribute: {}'.format(attr.name))

    def __dealloc__(self):
        pass

    @property
    def cptr(self):
        return <void_ptr>self._cptr
{{endif}}

{{if 'cudaJitOption' in found_types}}

cdef class _HelperCudaJitOption:
    def __cinit__(self, attr, init_value):
        self._attr = attr.value
        if self._attr in ({{if 'cudaJitMaxRegisters' in found_values}}cyruntime.cudaJitOption.cudaJitMaxRegisters,{{endif}}
                          {{if 'cudaJitThreadsPerBlock' in found_values}}cyruntime.cudaJitOption.cudaJitThreadsPerBlock,{{endif}}
                          {{if 'cudaJitInfoLogBufferSizeBytes' in found_values}}cyruntime.cudaJitOption.cudaJitInfoLogBufferSizeBytes,{{endif}}
                          {{if 'cudaJitErrorLogBufferSizeBytes' in found_values}}cyruntime.cudaJitOption.cudaJitErrorLogBufferSizeBytes,{{endif}}
                          {{if 'cudaJitOptimizationLevel' in found_values}}cyruntime.cudaJitOption.cudaJitOptimizationLevel,{{endif}}
                          {{if 'cudaJitMinCtaPerSm' in found_values}}cyruntime.cudaJitOption.cudaJitMinCtaPerSm,{{endif}}):
            self._uint = init_value
            self._cptr = <void*><void_ptr>self._uint
        elif self._attr in ({{if 'cudaJitWallTime' in found_values}}cyruntime.cudaJitOption.cudaJitWallTime,{{endif}}):
            self._float = init_value
            self._cptr = <void*><void_ptr>self._float
        elif self._attr in ({{if 'cudaJitInfoLogBuffer' in found_values}}cyruntime.cudaJitOption.cudaJitInfoLogBuffer,{{endif}}
                            {{if 'cudaJitErrorLogBuffer' in found_values}}cyruntime.cudaJitOption.cudaJitErrorLogBuffer{{endif}}):
            self._charstar = init_value
            self._cptr = <void*><void_ptr>self._charstar
        elif self._attr in ({{if 'cudaJitFallbackStrategy' in found_values}}cyruntime.cudaJitOption.cudaJitFallbackStrategy,{{endif}}):
            self._fallback = init_value.value
            self._cptr = <void*><void_ptr>self._fallback
        elif self._attr in ({{if 'cudaJitGenerateDebugInfo' in found_values}}cyruntime.cudaJitOption.cudaJitGenerateDebugInfo,{{endif}}
                            {{if 'cudaJitLogVerbose' in found_values}}cyruntime.cudaJitOption.cudaJitLogVerbose,{{endif}}
                            {{if 'cudaJitGenerateLineInfo' in found_values}}cyruntime.cudaJitOption.cudaJitGenerateLineInfo,{{endif}}
                            {{if 'cudaJitPositionIndependentCode' in found_values}}cyruntime.cudaJitOption.cudaJitPositionIndependentCode,{{endif}}
                            {{if 'cudaJitMaxThreadsPerBlock' in found_values}}cyruntime.cudaJitOption.cudaJitMaxThreadsPerBlock,{{endif}}
                            {{if 'cudaJitOverrideDirectiveValues' in found_values}}cyruntime.cudaJitOption.cudaJitOverrideDirectiveValues,{{endif}}):
            self._int = init_value
            self._cptr = <void*><void_ptr>self._int
        elif self._attr in ({{if 'cudaJitCacheMode' in found_values}}cyruntime.cudaJitOption.cudaJitCacheMode,{{endif}}):
            self._cacheMode = init_value.value
            self._cptr = <void*><void_ptr>self._cacheMode
        else:
            raise TypeError('Unsupported attribute: {}'.format(attr.name))

    def __dealloc__(self):
        pass

    @property
    def cptr(self):
        return <void_ptr>self._cptr
{{endif}}

{{if 'CUlibraryOption_enum' in found_types}}

cdef class _HelperCUlibraryOption:
    def __cinit__(self, attr, init_value):
        self._attr = attr.value
        if False:
            pass
        {{if 'CU_LIBRARY_HOST_UNIVERSAL_FUNCTION_AND_DATA_TABLE' in found_values}}
        elif self._attr in (cydriver.CUlibraryOption_enum.CU_LIBRARY_HOST_UNIVERSAL_FUNCTION_AND_DATA_TABLE,):
            self._cptr = <void*><void_ptr>init_value.getPtr()
        {{endif}}
        {{if 'CU_LIBRARY_BINARY_IS_PRESERVED' in found_values}}
        elif self._attr in (cydriver.CUlibraryOption_enum.CU_LIBRARY_BINARY_IS_PRESERVED,):
            self._uint = init_value
            self._cptr = <void*><void_ptr>self._uint
        {{endif}}
        else:
            raise TypeError('Unsupported attribute: {}'.format(attr.name))

    def __dealloc__(self):
        pass

    @property
    def cptr(self):
        return <void_ptr>self._cptr
{{endif}}

{{if 'cudaLibraryOption' in found_types}}

cdef class _HelperCudaLibraryOption:
    def __cinit__(self, attr, init_value):
        self._attr = attr.value
        if False:
            pass
        {{if 'cudaLibraryHostUniversalFunctionAndDataTable' in found_values}}
        elif self._attr in (cyruntime.cudaLibraryOption.cudaLibraryHostUniversalFunctionAndDataTable,):
            self._cptr = <void*><void_ptr>init_value.getPtr()
        {{endif}}
        {{if 'cudaLibraryBinaryIsPreserved' in found_values}}
        elif self._attr in (cyruntime.cudaLibraryOption.cudaLibraryBinaryIsPreserved,):
            self._uint = init_value
            self._cptr = <void*><void_ptr>self._uint
        {{endif}}
        else:
            raise TypeError('Unsupported attribute: {}'.format(attr.name))

    def __dealloc__(self):
        pass

    @property
    def cptr(self):
        return <void_ptr>self._cptr
{{endif}}

{{if 'CUmemAllocationHandleType_enum' in found_types}}

cdef class _HelperCUmemAllocationHandleType:
    def __cinit__(self, attr):
        self._type = attr.value
        if False:
            pass
        {{if 'CU_MEM_HANDLE_TYPE_NONE' in found_values}}
        elif self._type in (cydriver.CUmemAllocationHandleType_enum.CU_MEM_HANDLE_TYPE_NONE,):
            self._cptr = <void*>&self._int
        {{endif}}
        {{if 'CU_MEM_HANDLE_TYPE_POSIX_FILE_DESCRIPTOR' in found_values}}
        elif self._type in (cydriver.CUmemAllocationHandleType_enum.CU_MEM_HANDLE_TYPE_POSIX_FILE_DESCRIPTOR,):
            self._cptr = <void*>&self._int
        {{endif}}
        {{if 'CU_MEM_HANDLE_TYPE_WIN32' in found_values}}
        elif self._type in (cydriver.CUmemAllocationHandleType_enum.CU_MEM_HANDLE_TYPE_WIN32,):
            self._cptr = <void*>&self._handle
        {{endif}}
        {{if 'CU_MEM_HANDLE_TYPE_WIN32_KMT' in found_values}}
        elif self._type in (cydriver.CUmemAllocationHandleType_enum.CU_MEM_HANDLE_TYPE_WIN32_KMT,):
            self._cptr = <void*>&self._d3dkmt_handle
        {{endif}}
        {{if 'CU_MEM_HANDLE_TYPE_FABRIC' in found_values}}
        elif self._type in (cydriver.CUmemAllocationHandleType_enum.CU_MEM_HANDLE_TYPE_FABRIC,):
            self._mem_fabric_handle = _driver.CUmemFabricHandle()
            self._cptr = <void*><void_ptr>self._mem_fabric_handle.getPtr()
        {{endif}}
        else:
            raise TypeError('Unsupported attribute: {}'.format(attr.name))

    def __dealloc__(self):
        pass

    @property
    def cptr(self):
        return <void_ptr>self._cptr

    def pyObj(self):
        if False:
            pass
        {{if 'CU_MEM_HANDLE_TYPE_NONE' in found_values}}
        elif self._type in (cydriver.CUmemAllocationHandleType_enum.CU_MEM_HANDLE_TYPE_NONE,):
            return self._int
        {{endif}}
        {{if 'CU_MEM_HANDLE_TYPE_POSIX_FILE_DESCRIPTOR' in found_values}}
        elif self._type in (cydriver.CUmemAllocationHandleType_enum.CU_MEM_HANDLE_TYPE_POSIX_FILE_DESCRIPTOR,):
            return self._int
        {{endif}}
        {{if 'CU_MEM_HANDLE_TYPE_WIN32' in found_values}}
        elif self._type in (cydriver.CUmemAllocationHandleType_enum.CU_MEM_HANDLE_TYPE_WIN32,):
            return <void_ptr>self._handle
        {{endif}}
        {{if 'CU_MEM_HANDLE_TYPE_WIN32_KMT' in found_values}}
        elif self._type in (cydriver.CUmemAllocationHandleType_enum.CU_MEM_HANDLE_TYPE_WIN32_KMT,):
            return self._d3dkmt_handle
        {{endif}}
        {{if 'CU_MEM_HANDLE_TYPE_FABRIC' in found_values}}
        elif self._type in (cydriver.CUmemAllocationHandleType_enum.CU_MEM_HANDLE_TYPE_FABRIC,):
            return self._mem_fabric_handle
        {{endif}}
        else:
            raise TypeError('Unsupported attribute: {}'.format(self._type))
{{endif}}

cdef class _InputVoidPtrPtrHelper:
    def __cinit__(self, lst):
        # Hold onto references to the original buffers so they
        # won't be free'd behind our back
        self._references = lst
        self._cptr = <void**>_callocWrapper(len(lst), sizeof(void*))
        for idx in range(len(lst)):
            self._cptr[idx] = <void*><void_ptr>lst[idx].cptr

    def __dealloc__(self):
        free(self._cptr)

    @property
    def cptr(self):
        return <void_ptr>self._cptr

{{if 'CUcoredumpSettings_enum' in found_types}}

cdef class _HelperCUcoredumpSettings:
    def __cinit__(self, attr, init_value, is_getter=False):
        self._is_getter = is_getter
        self._attrib = attr.value
        if self._attrib in ({{if 'CU_COREDUMP_FILE' in found_values}}cydriver.CUcoredumpSettings_enum.CU_COREDUMP_FILE,{{endif}}
                          {{if 'CU_COREDUMP_PIPE' in found_values}}cydriver.CUcoredumpSettings_enum.CU_COREDUMP_PIPE,{{endif}}):
            if self._is_getter:
                self._charstar = <char*>_callocWrapper(1024, 1)
                self._cptr = <void*><void_ptr>self._charstar
                self._size = 1024
            else:
                self._charstar = init_value
                self._cptr = <void*><void_ptr>self._charstar
                self._size = len(init_value)
        elif self._attrib in ({{if 'CU_COREDUMP_ENABLE_ON_EXCEPTION' in found_values}}cydriver.CUcoredumpSettings_enum.CU_COREDUMP_ENABLE_ON_EXCEPTION,{{endif}}
                            {{if 'CU_COREDUMP_TRIGGER_HOST' in found_values}}cydriver.CUcoredumpSettings_enum.CU_COREDUMP_TRIGGER_HOST,{{endif}}
                            {{if 'CU_COREDUMP_LIGHTWEIGHT' in found_values}}cydriver.CUcoredumpSettings_enum.CU_COREDUMP_LIGHTWEIGHT,{{endif}}
                            {{if 'CU_COREDUMP_ENABLE_USER_TRIGGER' in found_values}}cydriver.CUcoredumpSettings_enum.CU_COREDUMP_ENABLE_USER_TRIGGER,{{endif}}):
            if self._is_getter == False:
                self._bool = init_value
            
            self._cptr = <void*>&self._bool
            self._size = 1
        else:
            raise TypeError('Unsupported attribute: {}'.format(attr.name))

    def __dealloc__(self):
        pass

    @property
    def cptr(self):
        return <void_ptr>self._cptr

    def size(self):
        return self._size

    def pyObj(self):
        assert(self._is_getter == True)
        if self._attrib in ({{if 'CU_COREDUMP_FILE' in found_values}}cydriver.CUcoredumpSettings_enum.CU_COREDUMP_FILE,{{endif}}
                          {{if 'CU_COREDUMP_PIPE' in found_values}}cydriver.CUcoredumpSettings_enum.CU_COREDUMP_PIPE,{{endif}}):
            return self._charstar
        elif self._attrib in ({{if 'CU_COREDUMP_ENABLE_ON_EXCEPTION' in found_values}}cydriver.CUcoredumpSettings_enum.CU_COREDUMP_ENABLE_ON_EXCEPTION,{{endif}}
                            {{if 'CU_COREDUMP_TRIGGER_HOST' in found_values}}cydriver.CUcoredumpSettings_enum.CU_COREDUMP_TRIGGER_HOST,{{endif}}
                            {{if 'CU_COREDUMP_LIGHTWEIGHT' in found_values}}cydriver.CUcoredumpSettings_enum.CU_COREDUMP_LIGHTWEIGHT,{{endif}}
                            {{if 'CU_COREDUMP_ENABLE_USER_TRIGGER' in found_values}}cydriver.CUcoredumpSettings_enum.CU_COREDUMP_ENABLE_USER_TRIGGER,{{endif}}):
            return self._bool
        else:
            raise TypeError('Unsupported attribute value: {}'.format(self._attrib))
{{endif}}
