def two_d_np_array_double_to_native(
        ffi: FFI,
        data: np.ndarray,
        shallow: bool = False) -> OwningCffiNativeHandle:
    """Convert if possible a cffi pointer to a C data array, into a numpy array of double precision floats.

    Args:
        ffi (FFI): FFI instance wrapping the native compilation module owning the native memory
        data (np.ndarray): data
        shallow (bool): If true the array points directly to native data array. Defaults to False.

    Raises:
        RuntimeError: conversion is not supported

    Returns:
        np.ndarray: converted data
    """
    if not isinstance(data, np.ndarray):
        raise TypeError("Expected np.ndarray, got " + str(type(data)))
    if not len(data.shape) < 3:
        raise TypeError("Expected an array of dimension 1 or 2, got " +
                        str(len(data.shape)))

    if len(data.shape) == 1:
        data = data.reshape((1, len(data)))

    nrow = data.shape[0]
    ptr = new_doubleptr_array(ffi, nrow)
    items = [as_c_double_array(ffi, data[i, :]).ptr for i in range(nrow)]
    for i in range(nrow):
        ptr[i] = items[i]
    result = OwningCffiNativeHandle(ptr)
    result.keepalive = items
    return result
def as_character_vector(ffi: FFI, obj: List[Any]) -> OwningCffiNativeHandle:
    """Convert a list of "strings" to a character_vector* native struct"""
    cv = ffi.new("character_vector*")
    cv.size = len(obj)
    names = as_arrayof_bytes(ffi, obj)
    cv.values = names.ptr
    result = OwningCffiNativeHandle(cv)
    result.keepalive = names
    return result
def dict_to_named_values(ffi: FFI,
                         data: Dict[str, float]) -> OwningCffiNativeHandle:
    ptr = ffi.new("named_values_vector*")
    ptr.size = len(data)
    ptr.values = as_c_double_array(ffi, list(data.values())).ptr
    names = as_arrayof_bytes(ffi, list(data.keys()))
    ptr.names = names.ptr
    result = OwningCffiNativeHandle(ptr)
    result.keepalive = names
    return result
def dict_to_string_map(ffi: FFI, data: Dict[str,
                                            str]) -> OwningCffiNativeHandle:
    ptr = ffi.new("string_string_map*")
    ptr.size = len(data)
    keys = as_arrayof_bytes(ffi, list(data.keys()))
    ptr.keys = keys.ptr
    values = as_arrayof_bytes(ffi, list(data.values()))
    ptr.values = values.ptr
    result = OwningCffiNativeHandle(ptr)
    result.keepalive = [keys, values]
    return result
def as_arrayof_bytes(ffi: FFI, obj: List[Any]) -> OwningCffiNativeHandle:
    """Convert a list of "strings" to a char** like C array

    Args:
        obj (List): list of objects (strings) to convert

    Returns:
        List: objects converted to bytes if it was a type of string
    """
    ptr = new_charptr_array(ffi, len(obj))
    items = [ffi.new("char[]", as_bytes(obj[i])) for i in range(len(obj))]
    for i in range(len(obj)):
        ptr[i] = items[i]
    result = OwningCffiNativeHandle(ptr)
    result.keepalive = items
    return result
def new_ctype_array(ffi: FFI,
                    ctype: str,
                    size: int,
                    wrap=False) -> Union[OwningCffiNativeHandle, CffiData]:
    x = ffi.new('%s[%d]' % (ctype, size))
    if wrap:
        return OwningCffiNativeHandle(x)
    else:
        return x
def as_native_time_series(ffi: FFI,
                          data: TimeSeriesLike) -> OwningCffiNativeHandle:
    ptr = ffi.new("multi_regular_time_series_data*")
    tsg = get_native_tsgeom(ffi, data)
    ptr.time_series_geometry = tsg.obj
    if isinstance(data, xr.DataArray):
        ensemble_size = len(data.coords[ENSEMBLE_DIMNAME].values)
        np_data = data.values
    elif isinstance(data, pd.Series):
        ensemble_size = 1
        np_data = data.values
    elif isinstance(data, pd.DataFrame):
        ensemble_size = data.shape[1]
        np_data = data.values.transpose()
    else:
        raise TypeError("Not recognised as a type of time series: " +
                        str(type(data)))
    ptr.ensemble_size = ensemble_size
    num_data = two_d_np_array_double_to_native(ffi, np_data)
    ptr.numeric_data = num_data.ptr
    result = OwningCffiNativeHandle(ptr)
    result.keepalive = [tsg, num_data]
    return result
def as_c_char_array(ffi,
                    data,
                    shallow: bool = False) -> OwningCffiNativeHandle:
    if isinstance(data, list):
        data = np.asfarray(data)
        shallow = False
    elif not isinstance(data, np.ndarray):
        raise TypeError(
            "Conversion to a c array of double requires list or np array as input"
        )
    if shallow and data.flags['C_CONTIGUOUS']:
        native_d = ffi.cast("char *", data.ctypes.data)
    else:
        native_d = new_charptr_array(ffi, data.shape[0])
        if not data.flags['C_CONTIGUOUS']:
            data_c = np.ascontiguousarray(data.ctypes.data)
        else:
            data_c = data
        ffi.buffer(native_d)[:] = data_c
    return OwningCffiNativeHandle(native_d)
def as_c_double_array(ffi: FFI,
                      data: np.ndarray,
                      shallow: bool = False) -> OwningCffiNativeHandle:
    if isinstance(data, list):
        data = np.asfarray(data)
        shallow = False
    elif isinstance(data, xr.DataArray):
        data = data.values
        # shallow = False # really needed??
    elif not isinstance(data, np.ndarray):
        raise TypeError(
            "Conversion to a c array of double requires list or np array as input"
        )
    if len(data.shape) > 1:
        data = data.squeeze()
        shallow = False
        if len(data.shape) > 1:
            raise TypeError(
                "Conversion to a double* array: input data must be of dimension one, and the python array cannot be squeezed to dimension one"
            )
    if not (data.dtype == np.float or data.dtype == np.float64 or data.dtype
            == float or data.dtype == np.double or data.dtype == np.float_):
        # https://numpy.org/devdocs/release/1.20.0-notes.html#deprecations
        # TODO: is this wise to override the shallow parameter
        shallow = False
        data = data.astype(np.float64)
    if shallow and data.flags['C_CONTIGUOUS']:
        native_d = ffi.cast("double *", data.ctypes.data)
    else:
        native_d = new_double_array(ffi, data.shape[0])
        if not data.flags['C_CONTIGUOUS']:
            data_c = np.ascontiguousarray(data)
        else:
            data_c = data
        ffi.buffer(native_d)[:] = data_c
    return OwningCffiNativeHandle(native_d)
def new_date_time_to_second(ffi: FFI) -> OwningCffiNativeHandle:
    ptr = ffi.new("date_time_to_second*")
    return OwningCffiNativeHandle(ptr)
def datetime_to_dtts(ffi: FFI, dt: datetime) -> OwningCffiNativeHandle:
    ptr = ffi.new("date_time_to_second*")
    _copy_datetime_to_dtts(dt, ptr)
    return OwningCffiNativeHandle(ptr)
def as_charptr(ffi: FFI, x: str, wrap=False) -> CffiData:
    x = ffi.new("char[]", as_bytes(x))
    if wrap:
        return OwningCffiNativeHandle(x)
    else:
        return x
def create_values_struct(ffi: FFI, data: np.ndarray) -> OwningCffiNativeHandle:
    ptr = ffi.new("values_vector*")
    ptr.size = len(data)
    ptr.values = as_c_double_array(ffi, data).ptr
    return OwningCffiNativeHandle(ptr)
 def new_native_struct(self, type) -> OwningCffiNativeHandle:
     return OwningCffiNativeHandle(self._ffi.new(type), type)