IMMREX7
# coding: utf-8
"""
Exceptions and compatibility shims for consistently using ctypes and cffi
"""
from __future__ import unicode_literals, division, absolute_import, print_function
import sys
import platform
from ctypes.util import find_library
from . import ffi
from ._types import str_cls, byte_cls, int_types, bytes_to_list
__all__ = [
'array_from_pointer',
'array_set',
'buffer_from_bytes',
'buffer_from_unicode',
'buffer_pointer',
'byte_array',
'byte_string_from_buffer',
'bytes_from_buffer',
'callback',
'cast',
'deref',
'errno',
'FFIEngineError',
'get_library',
'is_null',
'native',
'new',
'null',
'pointer_set',
'ref',
'register_ffi',
'sizeof',
'struct',
'struct_bytes',
'struct_from_buffer',
'unwrap',
'write_to_buffer',
]
if ffi() == 'cffi':
from cffi import FFI
_ffi_registry = {}
ffi = FFI()
def register_ffi(library, ffi_obj):
_ffi_registry[library] = ffi_obj
def _get_ffi(library):
if library in _ffi_registry:
return _ffi_registry[library]
return ffi
def buffer_from_bytes(initializer):
if sys.platform == 'win32':
return ffi.new('unsigned char[]', initializer)
return ffi.new('char[]', initializer)
def buffer_from_unicode(initializer):
return ffi.new('wchar_t []', initializer)
def write_to_buffer(buffer, data, offset=0):
buffer[offset:offset + len(data)] = data
def buffer_pointer(buffer):
return ffi.new('char *[]', [buffer])
def cast(library, type_, value):
ffi_obj = _get_ffi(library)
return ffi_obj.cast(type_, value)
def sizeof(library, value):
ffi_obj = _get_ffi(library)
return ffi_obj.sizeof(value)
def bytes_from_buffer(buffer, maxlen=None):
if maxlen is not None:
return ffi.buffer(buffer, maxlen)[:]
return ffi.buffer(buffer)[:]
def byte_string_from_buffer(buffer):
return ffi.string(buffer)
def byte_array(byte_string):
return byte_string
def pointer_set(pointer_, value):
pointer_[0] = value
def array_set(array, value):
for index, val in enumerate(value):
array[index] = val
def null():
return ffi.NULL
def is_null(point):
if point is None:
return True
if point == ffi.NULL:
return True
if ffi.getctype(ffi.typeof(point)) == 'void *':
return False
if point[0] == ffi.NULL:
return True
return False
def errno():
return ffi.errno
def new(library, type_, value=None):
ffi_obj = _get_ffi(library)
params = []
if value is not None:
params.append(value)
if type_ in set(['BCRYPT_KEY_HANDLE', 'BCRYPT_ALG_HANDLE']):
return ffi_obj.cast(type_, 0)
return ffi_obj.new(type_, *params)
def ref(value, offset=0):
return value + offset
def native(type_, value):
if type_ == str_cls:
return ffi.string(value)
if type_ == byte_cls:
return ffi.buffer(value)[:]
return type_(value)
def deref(point):
return point[0]
def unwrap(point):
return point[0]
def struct(library, name):
ffi_obj = _get_ffi(library)
return ffi_obj.new('%s *' % name)
def struct_bytes(struct_):
return ffi.buffer(struct_)[:]
def struct_from_buffer(library, name, buffer):
ffi_obj = _get_ffi(library)
new_struct_pointer = ffi_obj.new('%s *' % name)
new_struct = new_struct_pointer[0]
struct_size = sizeof(library, new_struct)
struct_buffer = ffi_obj.buffer(new_struct_pointer)
struct_buffer[:] = ffi_obj.buffer(buffer, struct_size)[:]
return new_struct_pointer
def array_from_pointer(library, name, point, size):
ffi_obj = _get_ffi(library)
array = ffi_obj.cast('%s[%s]' % (name, size), point)
total_bytes = ffi_obj.sizeof(array)
if total_bytes == 0:
return []
output = []
string_types = {
'LPSTR': True,
'LPCSTR': True,
'LPWSTR': True,
'LPCWSTR': True,
'char *': True,
'wchar_t *': True,
}
string_type = name in string_types
for i in range(0, size):
value = array[i]
if string_type:
value = ffi_obj.string(value)
output.append(value)
return output
def callback(library, signature_name, func):
ffi_obj = _get_ffi(library)
return ffi_obj.callback(signature_name, func)
engine = 'cffi'
else:
import ctypes
from ctypes import pointer, c_int, c_char_p, c_uint, c_void_p, c_wchar_p
_pointer_int_types = int_types + (c_char_p, ctypes.POINTER(ctypes.c_byte))
_pointer_types = {
'void *': True,
'wchar_t *': True,
'char *': True,
'char **': True,
}
_type_map = {
'void *': c_void_p,
'wchar_t *': c_wchar_p,
'char *': c_char_p,
'char **': ctypes.POINTER(c_char_p),
'int': c_int,
'unsigned int': c_uint,
'size_t': ctypes.c_size_t,
'uint32_t': ctypes.c_uint32,
}
if sys.platform == 'win32':
from ctypes import wintypes
_pointer_types.update({
'LPSTR': True,
'LPWSTR': True,
'LPCSTR': True,
'LPCWSTR': True,
})
_type_map.update({
'BYTE': ctypes.c_byte,
'LPSTR': c_char_p,
'LPWSTR': c_wchar_p,
'LPCSTR': c_char_p,
'LPCWSTR': c_wchar_p,
'ULONG': wintypes.ULONG,
'DWORD': wintypes.DWORD,
'char *': ctypes.POINTER(ctypes.c_byte),
'char **': ctypes.POINTER(ctypes.POINTER(ctypes.c_byte)),
})
def _type_info(library, type_):
is_double_pointer = type_[-3:] == ' **'
if is_double_pointer:
type_ = type_[:-1]
is_pointer = type_[-2:] == ' *' and type_ not in _pointer_types
if is_pointer:
type_ = type_[:-2]
is_array = type_.find('[') != -1
if is_array:
is_array = type_[type_.find('[') + 1:type_.find(']')]
if is_array == '':
is_array = True
else:
is_array = int(is_array)
type_ = type_[0:type_.find('[')]
if type_ in _type_map:
type_ = _type_map[type_]
else:
type_ = getattr(library, type_)
if is_double_pointer:
type_ = ctypes.POINTER(type_)
return (is_pointer, is_array, type_)
def register_ffi(library, ffi_obj):
pass
def buffer_from_bytes(initializer):
return ctypes.create_string_buffer(initializer)
def buffer_from_unicode(initializer):
return ctypes.create_unicode_buffer(initializer)
def write_to_buffer(buffer, data, offset=0):
if isinstance(buffer, ctypes.POINTER(ctypes.c_byte)):
ctypes.memmove(buffer, data, len(data))
return
if offset == 0:
buffer.value = data
else:
buffer.value = buffer.raw[0:offset] + data
def buffer_pointer(buffer):
return pointer(ctypes.cast(buffer, c_char_p))
def cast(library, type_, value):
is_pointer, is_array, type_ = _type_info(library, type_)
if is_pointer:
type_ = ctypes.POINTER(type_)
elif is_array:
type_ = type_ * is_array
return ctypes.cast(value, type_)
def sizeof(library, value):
return ctypes.sizeof(value)
def bytes_from_buffer(buffer, maxlen=None):
if isinstance(buffer, _pointer_int_types):
return ctypes.string_at(buffer, maxlen)
if maxlen is not None:
return buffer.raw[0:maxlen]
return buffer.raw
def byte_string_from_buffer(buffer):
return buffer.value
def byte_array(byte_string):
return (ctypes.c_byte * len(byte_string))(*bytes_to_list(byte_string))
def pointer_set(pointer_, value):
pointer_.contents.value = value
def array_set(array, value):
for index, val in enumerate(value):
array[index] = val
def null():
return None
def is_null(point):
return not bool(point)
def errno():
return ctypes.get_errno()
def new(library, type_, value=None):
is_pointer, is_array, type_ = _type_info(library, type_)
if is_array:
if is_array is True:
type_ = type_ * value
value = None
else:
type_ = type_ * is_array
params = []
if value is not None:
params.append(value)
output = type_(*params)
if is_pointer:
output = pointer(output)
return output
def ref(value, offset=0):
if offset == 0:
return ctypes.byref(value)
return ctypes.cast(ctypes.addressof(value) + offset, ctypes.POINTER(ctypes.c_byte))
def native(type_, value):
if isinstance(value, type_):
return value
if sys.version_info < (3,) and type_ == int and isinstance(value, int_types):
return value
if isinstance(value, ctypes.Array) and value._type_ == ctypes.c_byte:
return ctypes.string_at(ctypes.addressof(value), value._length_)
return type_(value.value)
def deref(point):
return point[0]
def unwrap(point):
return point.contents
def struct(library, name):
return pointer(getattr(library, name)())
def struct_bytes(struct_):
return ctypes.string_at(struct_, ctypes.sizeof(struct_.contents))
def struct_from_buffer(library, type_, buffer):
class_ = getattr(library, type_)
value = class_()
ctypes.memmove(ctypes.addressof(value), buffer, ctypes.sizeof(class_))
return ctypes.pointer(value)
def array_from_pointer(library, type_, point, size):
_, _, type_ = _type_info(library, type_)
array = ctypes.cast(point, ctypes.POINTER(type_))
output = []
for i in range(0, size):
output.append(array[i])
return output
def callback(library, signature_type, func):
return getattr(library, signature_type)(func)
engine = 'ctypes'
def get_library(name, dylib_name, version):
"""
Retrieve the C library path with special handling for Mac
:param name:
A unicode string of the library to search the system for
:param dylib_name:
Mac only - a unicode string of the unversioned dylib name
:param version:
Mac only - a unicode string of the dylib version to use. Used on macOS
10.15+ when the unversioned dylib is found, since unversioned
OpenSSL/LibreSSL are just placeholders, and a versioned dylib must be
imported. Used on macOS 10.16+ when find_library() doesn't return a
result, due to system dylibs not being present on the filesystem any
longer.
:return:
A unicode string of the path to the library
"""
library = find_library(name)
if sys.platform == 'darwin':
unversioned = '/usr/lib/%s' % dylib_name
versioned = unversioned.replace('.dylib', '.%s.dylib' % version)
mac_ver = tuple(map(int, platform.mac_ver()[0].split('.')))
if not library and mac_ver >= (10, 16):
# On macOS 10.16+, find_library doesn't work, so we set a static path
library = versioned
elif mac_ver >= (10, 15) and library == unversioned:
# On macOS 10.15+, we want to strongly version since unversioned libcrypto has a non-stable ABI
library = versioned
return library
class FFIEngineError(Exception):
"""
An exception when trying to instantiate ctypes or cffi
"""
pass
Copyright © 2021 -