feat: inline cast uint32 and int16 to speed up unmarshall (#115)
This commit is contained in:
4
src/dbus_fast/_private/_cython_compat.py
Normal file
4
src/dbus_fast/_private/_cython_compat.py
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
class FakeCython:
|
||||||
|
@property
|
||||||
|
def compiled(self):
|
||||||
|
return False
|
||||||
@@ -14,6 +14,8 @@ cdef unsigned int BIG_ENDIAN
|
|||||||
cdef unsigned int PROTOCOL_VERSION
|
cdef unsigned int PROTOCOL_VERSION
|
||||||
cdef str UINT32_CAST
|
cdef str UINT32_CAST
|
||||||
cdef str INT16_CAST
|
cdef str INT16_CAST
|
||||||
|
cdef bint SYS_IS_LITTLE_ENDIAN
|
||||||
|
cdef bint SYS_IS_BIG_ENDIAN
|
||||||
|
|
||||||
cdef object UNPACK_HEADER_LITTLE_ENDIAN
|
cdef object UNPACK_HEADER_LITTLE_ENDIAN
|
||||||
cdef object UNPACK_HEADER_BIG_ENDIAN
|
cdef object UNPACK_HEADER_BIG_ENDIAN
|
||||||
@@ -30,6 +32,13 @@ cdef object HEADER_MESSAGE_ARG_NAME
|
|||||||
|
|
||||||
cpdef get_signature_tree
|
cpdef get_signature_tree
|
||||||
|
|
||||||
|
cdef inline unsigned long _cast_uint32_native(const char * payload, unsigned int offset):
|
||||||
|
cdef unsigned long *u32p = <unsigned long *> &payload[offset]
|
||||||
|
return u32p[0]
|
||||||
|
|
||||||
|
cdef inline short _cast_int16_native(const char * payload, unsigned int offset):
|
||||||
|
cdef short *s16p = <short *> &payload[offset]
|
||||||
|
return s16p[0]
|
||||||
|
|
||||||
cdef class MarshallerStreamEndError(Exception):
|
cdef class MarshallerStreamEndError(Exception):
|
||||||
pass
|
pass
|
||||||
@@ -49,6 +58,7 @@ cdef class Unmarshaller:
|
|||||||
cdef unsigned int _message_type
|
cdef unsigned int _message_type
|
||||||
cdef unsigned int _flag
|
cdef unsigned int _flag
|
||||||
cdef unsigned int _msg_len
|
cdef unsigned int _msg_len
|
||||||
|
cdef unsigned int _is_native
|
||||||
cdef object _uint32_unpack
|
cdef object _uint32_unpack
|
||||||
cdef object _int16_unpack
|
cdef object _int16_unpack
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import array
|
import array
|
||||||
import io
|
import io
|
||||||
import pprint
|
|
||||||
import socket
|
import socket
|
||||||
import sys
|
import sys
|
||||||
from struct import Struct
|
from struct import Struct
|
||||||
@@ -24,6 +23,9 @@ INT16_CAST = "h"
|
|||||||
INT16_SIZE = 2
|
INT16_SIZE = 2
|
||||||
INT16_DBUS_TYPE = "n"
|
INT16_DBUS_TYPE = "n"
|
||||||
|
|
||||||
|
SYS_IS_LITTLE_ENDIAN = sys.byteorder == "little"
|
||||||
|
SYS_IS_BIG_ENDIAN = sys.byteorder == "big"
|
||||||
|
|
||||||
DBUS_TO_CTYPE = {
|
DBUS_TO_CTYPE = {
|
||||||
"y": ("B", 1), # byte
|
"y": ("B", 1), # byte
|
||||||
INT16_DBUS_TYPE: (INT16_CAST, INT16_SIZE), # int16
|
INT16_DBUS_TYPE: (INT16_CAST, INT16_SIZE), # int16
|
||||||
@@ -99,6 +101,11 @@ class MarshallerStreamEndError(Exception):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
try:
|
||||||
|
import cython
|
||||||
|
except ImportError:
|
||||||
|
from ._cython_compat import FakeCython as cython
|
||||||
|
|
||||||
#
|
#
|
||||||
# Alignment padding is handled with the following formula below
|
# Alignment padding is handled with the following formula below
|
||||||
#
|
#
|
||||||
@@ -134,6 +141,7 @@ class Unmarshaller:
|
|||||||
"_msg_len",
|
"_msg_len",
|
||||||
"_uint32_unpack",
|
"_uint32_unpack",
|
||||||
"_int16_unpack",
|
"_int16_unpack",
|
||||||
|
"_is_native",
|
||||||
)
|
)
|
||||||
|
|
||||||
def __init__(self, stream: io.BufferedRWPair, sock: Optional[socket.socket] = None):
|
def __init__(self, stream: io.BufferedRWPair, sock: Optional[socket.socket] = None):
|
||||||
@@ -150,6 +158,7 @@ class Unmarshaller:
|
|||||||
self._message_type = 0
|
self._message_type = 0
|
||||||
self._flag = 0
|
self._flag = 0
|
||||||
self._msg_len = 0
|
self._msg_len = 0
|
||||||
|
self._is_native = 0
|
||||||
self._uint32_unpack: Callable | None = None
|
self._uint32_unpack: Callable | None = None
|
||||||
self._int16_unpack: Callable | None = None
|
self._int16_unpack: Callable | None = None
|
||||||
|
|
||||||
@@ -168,6 +177,7 @@ class Unmarshaller:
|
|||||||
self._message_type = 0
|
self._message_type = 0
|
||||||
self._flag = 0
|
self._flag = 0
|
||||||
self._msg_len = 0
|
self._msg_len = 0
|
||||||
|
self._is_native = 0
|
||||||
self._uint32_unpack = None
|
self._uint32_unpack = None
|
||||||
self._int16_unpack = None
|
self._int16_unpack = None
|
||||||
|
|
||||||
@@ -230,6 +240,10 @@ class Unmarshaller:
|
|||||||
|
|
||||||
def _read_uint32_unpack(self) -> int:
|
def _read_uint32_unpack(self) -> int:
|
||||||
self._pos += UINT32_SIZE + (-self._pos & (UINT32_SIZE - 1)) # align
|
self._pos += UINT32_SIZE + (-self._pos & (UINT32_SIZE - 1)) # align
|
||||||
|
if self._is_native and cython.compiled:
|
||||||
|
return _cast_uint32_native( # pragma: no cover
|
||||||
|
self._buf, self._pos - UINT32_SIZE
|
||||||
|
)
|
||||||
return self._uint32_unpack(self._buf, self._pos - UINT32_SIZE)[0]
|
return self._uint32_unpack(self._buf, self._pos - UINT32_SIZE)[0]
|
||||||
|
|
||||||
def read_int16_unpack(self, type_: SignatureType) -> int:
|
def read_int16_unpack(self, type_: SignatureType) -> int:
|
||||||
@@ -237,6 +251,10 @@ class Unmarshaller:
|
|||||||
|
|
||||||
def _read_int16_unpack(self) -> int:
|
def _read_int16_unpack(self) -> int:
|
||||||
self._pos += INT16_SIZE + (-self._pos & (INT16_SIZE - 1)) # align
|
self._pos += INT16_SIZE + (-self._pos & (INT16_SIZE - 1)) # align
|
||||||
|
if self._is_native and cython.compiled:
|
||||||
|
return _cast_int16_native( # pragma: no cover
|
||||||
|
self._buf, self._pos - INT16_SIZE
|
||||||
|
)
|
||||||
return self._int16_unpack(self._buf, self._pos - INT16_SIZE)[0]
|
return self._int16_unpack(self._buf, self._pos - INT16_SIZE)[0]
|
||||||
|
|
||||||
def read_boolean(self, type_: SignatureType) -> bool:
|
def read_boolean(self, type_: SignatureType) -> bool:
|
||||||
@@ -250,7 +268,12 @@ class Unmarshaller:
|
|||||||
self._pos += UINT32_SIZE + (-self._pos & (UINT32_SIZE - 1)) # align
|
self._pos += UINT32_SIZE + (-self._pos & (UINT32_SIZE - 1)) # align
|
||||||
str_start = self._pos
|
str_start = self._pos
|
||||||
# read terminating '\0' byte as well (str_length + 1)
|
# read terminating '\0' byte as well (str_length + 1)
|
||||||
self._pos += self._uint32_unpack(self._buf, str_start - UINT32_SIZE)[0] + 1
|
if self._is_native and cython.compiled:
|
||||||
|
self._pos += ( # pragma: no cover
|
||||||
|
_cast_uint32_native(self._buf, str_start - UINT32_SIZE) + 1
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
self._pos += self._uint32_unpack(self._buf, str_start - UINT32_SIZE)[0] + 1
|
||||||
return self._buf[str_start : self._pos - 1].decode()
|
return self._buf[str_start : self._pos - 1].decode()
|
||||||
|
|
||||||
def read_signature(self, type_: SignatureType) -> str:
|
def read_signature(self, type_: SignatureType) -> str:
|
||||||
@@ -302,7 +325,12 @@ class Unmarshaller:
|
|||||||
self._pos += (
|
self._pos += (
|
||||||
-self._pos & (UINT32_SIZE - 1)
|
-self._pos & (UINT32_SIZE - 1)
|
||||||
) + UINT32_SIZE # align for the uint32
|
) + UINT32_SIZE # align for the uint32
|
||||||
array_length = self._uint32_unpack(self._buf, self._pos - UINT32_SIZE)[0]
|
if self._is_native and cython.compiled:
|
||||||
|
array_length = _cast_uint32_native( # pragma: no cover
|
||||||
|
self._buf, self._pos - UINT32_SIZE
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
array_length = self._uint32_unpack(self._buf, self._pos - UINT32_SIZE)[0]
|
||||||
|
|
||||||
child_type = type_.children[0]
|
child_type = type_.children[0]
|
||||||
token = child_type.token
|
token = child_type.token
|
||||||
@@ -398,6 +426,14 @@ class Unmarshaller:
|
|||||||
f"got unknown protocol version: {protocol_version}"
|
f"got unknown protocol version: {protocol_version}"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if cython.compiled and (
|
||||||
|
(endian == LITTLE_ENDIAN and SYS_IS_LITTLE_ENDIAN)
|
||||||
|
or (endian == BIG_ENDIAN and SYS_IS_BIG_ENDIAN)
|
||||||
|
):
|
||||||
|
self._is_native = 1 # pragma: no cover
|
||||||
|
self._body_len = _cast_uint32_native(self._buf, 4) # pragma: no cover
|
||||||
|
self._serial = _cast_uint32_native(self._buf, 8) # pragma: no cover
|
||||||
|
self._header_len = _cast_uint32_native(self._buf, 12) # pragma: no cover
|
||||||
if endian == LITTLE_ENDIAN:
|
if endian == LITTLE_ENDIAN:
|
||||||
(
|
(
|
||||||
self._body_len,
|
self._body_len,
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ from unittest.mock import patch
|
|||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from dbus_fast import Message, MessageFlag, MessageType, SignatureTree, Variant
|
from dbus_fast import Message, MessageFlag, MessageType, SignatureTree, Variant
|
||||||
|
from dbus_fast._private._cython_compat import FakeCython
|
||||||
from dbus_fast._private.unmarshaller import Unmarshaller
|
from dbus_fast._private.unmarshaller import Unmarshaller
|
||||||
|
|
||||||
|
|
||||||
@@ -171,3 +172,7 @@ def test_ay_buffer():
|
|||||||
marshalled = msg._marshall(False)
|
marshalled = msg._marshall(False)
|
||||||
unmarshalled_msg = Unmarshaller(io.BytesIO(marshalled)).unmarshall()
|
unmarshalled_msg = Unmarshaller(io.BytesIO(marshalled)).unmarshall()
|
||||||
assert unmarshalled_msg.body[0] == body[0]
|
assert unmarshalled_msg.body[0] == body[0]
|
||||||
|
|
||||||
|
|
||||||
|
def tests_fallback_no_cython():
|
||||||
|
assert FakeCython().compiled is False
|
||||||
|
|||||||
Reference in New Issue
Block a user