feat: inline cast uint32 and int16 to speed up unmarshall (#115)
This commit is contained in:
parent
5c2826d2f1
commit
24dd9d9742
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 str UINT32_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_BIG_ENDIAN
|
||||
@ -30,6 +32,13 @@ cdef object HEADER_MESSAGE_ARG_NAME
|
||||
|
||||
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):
|
||||
pass
|
||||
@ -49,6 +58,7 @@ cdef class Unmarshaller:
|
||||
cdef unsigned int _message_type
|
||||
cdef unsigned int _flag
|
||||
cdef unsigned int _msg_len
|
||||
cdef unsigned int _is_native
|
||||
cdef object _uint32_unpack
|
||||
cdef object _int16_unpack
|
||||
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
import array
|
||||
import io
|
||||
import pprint
|
||||
import socket
|
||||
import sys
|
||||
from struct import Struct
|
||||
@ -24,6 +23,9 @@ INT16_CAST = "h"
|
||||
INT16_SIZE = 2
|
||||
INT16_DBUS_TYPE = "n"
|
||||
|
||||
SYS_IS_LITTLE_ENDIAN = sys.byteorder == "little"
|
||||
SYS_IS_BIG_ENDIAN = sys.byteorder == "big"
|
||||
|
||||
DBUS_TO_CTYPE = {
|
||||
"y": ("B", 1), # byte
|
||||
INT16_DBUS_TYPE: (INT16_CAST, INT16_SIZE), # int16
|
||||
@ -99,6 +101,11 @@ class MarshallerStreamEndError(Exception):
|
||||
pass
|
||||
|
||||
|
||||
try:
|
||||
import cython
|
||||
except ImportError:
|
||||
from ._cython_compat import FakeCython as cython
|
||||
|
||||
#
|
||||
# Alignment padding is handled with the following formula below
|
||||
#
|
||||
@ -134,6 +141,7 @@ class Unmarshaller:
|
||||
"_msg_len",
|
||||
"_uint32_unpack",
|
||||
"_int16_unpack",
|
||||
"_is_native",
|
||||
)
|
||||
|
||||
def __init__(self, stream: io.BufferedRWPair, sock: Optional[socket.socket] = None):
|
||||
@ -150,6 +158,7 @@ class Unmarshaller:
|
||||
self._message_type = 0
|
||||
self._flag = 0
|
||||
self._msg_len = 0
|
||||
self._is_native = 0
|
||||
self._uint32_unpack: Callable | None = None
|
||||
self._int16_unpack: Callable | None = None
|
||||
|
||||
@ -168,6 +177,7 @@ class Unmarshaller:
|
||||
self._message_type = 0
|
||||
self._flag = 0
|
||||
self._msg_len = 0
|
||||
self._is_native = 0
|
||||
self._uint32_unpack = None
|
||||
self._int16_unpack = None
|
||||
|
||||
@ -230,6 +240,10 @@ class Unmarshaller:
|
||||
|
||||
def _read_uint32_unpack(self) -> int:
|
||||
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]
|
||||
|
||||
def read_int16_unpack(self, type_: SignatureType) -> int:
|
||||
@ -237,6 +251,10 @@ class Unmarshaller:
|
||||
|
||||
def _read_int16_unpack(self) -> int:
|
||||
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]
|
||||
|
||||
def read_boolean(self, type_: SignatureType) -> bool:
|
||||
@ -250,7 +268,12 @@ class Unmarshaller:
|
||||
self._pos += UINT32_SIZE + (-self._pos & (UINT32_SIZE - 1)) # align
|
||||
str_start = self._pos
|
||||
# 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()
|
||||
|
||||
def read_signature(self, type_: SignatureType) -> str:
|
||||
@ -302,7 +325,12 @@ class Unmarshaller:
|
||||
self._pos += (
|
||||
-self._pos & (UINT32_SIZE - 1)
|
||||
) + 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]
|
||||
token = child_type.token
|
||||
@ -398,6 +426,14 @@ class Unmarshaller:
|
||||
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:
|
||||
(
|
||||
self._body_len,
|
||||
|
||||
@ -7,6 +7,7 @@ from unittest.mock import patch
|
||||
import pytest
|
||||
|
||||
from dbus_fast import Message, MessageFlag, MessageType, SignatureTree, Variant
|
||||
from dbus_fast._private._cython_compat import FakeCython
|
||||
from dbus_fast._private.unmarshaller import Unmarshaller
|
||||
|
||||
|
||||
@ -171,3 +172,7 @@ def test_ay_buffer():
|
||||
marshalled = msg._marshall(False)
|
||||
unmarshalled_msg = Unmarshaller(io.BytesIO(marshalled)).unmarshall()
|
||||
assert unmarshalled_msg.body[0] == body[0]
|
||||
|
||||
|
||||
def tests_fallback_no_cython():
|
||||
assert FakeCython().compiled is False
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user