Skip to content

Commit

Permalink
feat: optimize for interfaces added messages (#122)
Browse files Browse the repository at this point in the history
  • Loading branch information
bdraco authored Oct 29, 2022
1 parent 864843c commit c05a27a
Show file tree
Hide file tree
Showing 4 changed files with 196 additions and 11 deletions.
40 changes: 40 additions & 0 deletions bench/unmarshall_interfaces_added.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import io
import timeit

from dbus_fast._private.unmarshaller import Unmarshaller

# cythonize -X language_level=3 -a -i src/dbus_fast/_private/unmarshaller.py


bluez_interfaces_added_message = (
b'l\4\1\1\240\2\0\0\227\272\23\0u\0\0\0\1\1o\0\1\0\0\0/\0\0\0\0\0\0\0\2\1s\0"\0\0\0'
b"org.freedesktop.DBus.ObjectManager\0\0\0\0\0\0\3\1s\0\17\0\0\0InterfacesAdded\0\10"
b"\1g\0\noa{sa{sv}}\0\7\1s\0\4\0\0\0:1.4\0\0\0\0%\0\0\0/org/bluez/hci1/dev_58_2D_34"
b"_60_26_36\0\0\0p\2\0\0#\0\0\0org.freedesktop.DBus.Introspectable\0\0\0\0\0\0\0\0\0"
b"\21\0\0\0org.bluez.Device1\0\0\0\364\1\0\0\0\0\0\0\7\0\0\0Address\0\1s\0\0\21\0\0"
b"\00058:2D:34:60:26:36\0\0\0\v\0\0\0AddressType\0\1s\0\0\6\0\0\0public\0\0\4\0\0\0"
b"Name\0\1s\0\33\0\0\0Qingping Door/Window Sensor\0\0\0\0\0\5\0\0\0Alias\0\1s\0\0\0"
b"\0\33\0\0\0Qingping Door/Window Sensor\0\6\0\0\0Paired\0\1b\0\0\0\0\0\0\0\0\0\0\0"
b"\7\0\0\0Trusted\0\1b\0\0\0\0\0\0\0\0\0\0\7\0\0\0Blocked\0\1b\0\0\0\0\0\0\0\0\0\0\r"
b"\0\0\0LegacyPairing\0\1b\0\0\0\0\0\0\0\0\0\0\0\0\4\0\0\0RSSI\0\1n\0\316\377\0\0\t"
b"\0\0\0Connected\0\1b\0\0\0\0\0\0\0\0\5\0\0\0UUIDs\0\2as\0\0\0\0\0\0\0\0\0\0\0\7\0"
b"\0\0Adapter\0\1o\0\0\17\0\0\0/org/bluez/hci1\0\0\0\0\0\v\0\0\0ServiceData\0\5a{sv}"
b"\0\0@\0\0\0\0\0\0\0$\0\0\0000000fe95-0000-1000-8000-00805f9b34fb\0\2ay\0\0\0\0\f\0"
b"\0\0000X\326\3\0026&`4-X\10\20\0\0\0ServicesResolved\0\1b\0\0\0\0\0\0\0\0\0\37\0\0"
b"\0org.freedesktop.DBus.Properties\0\0\0\0\0"
)


stream = io.BytesIO(bluez_interfaces_added_message)
unmarshaller = Unmarshaller(stream)


def unmarshall_interfaces_added_message():
stream.seek(0)
unmarshaller.reset()
unmarshaller.unmarshall()


count = 3000000
time = timeit.Timer(unmarshall_interfaces_added_message).timeit(count)
print(f"Unmarshalling {count} bluetooth InterfacesAdded messages took {time} seconds")
12 changes: 12 additions & 0 deletions src/dbus_fast/_private/unmarshaller.pxd
Original file line number Diff line number Diff line change
Expand Up @@ -41,11 +41,19 @@ cdef object MESSAGE_FLAG_MAP
cdef object HEADER_MESSAGE_ARG_NAME

cdef object SIGNATURE_TREE_EMPTY
cdef object SIGNATURE_TREE_B
cdef object SIGNATURE_TREE_N
cdef object SIGNATURE_TREE_O
cdef object SIGNATURE_TREE_S
cdef object SIGNATURE_TREE_AS
cdef object SIGNATURE_TREE_AS_TYPES_0
cdef object SIGNATURE_TREE_A_SV
cdef object SIGNATURE_TREE_A_SV_TYPES_0
cdef object SIGNATURE_TREE_SA_SV_AS
cdef object SIGNATURE_TREE_SA_SV_AS_TYPES_1
cdef object SIGNATURE_TREE_SA_SV_AS_TYPES_2
cdef object SIGNATURE_TREE_OA_SA_SV
cdef object SIGNATURE_TREE_OA_SA_SV_TYPES_1
cdef object SIGNATURE_TREE_AY
cdef object SIGNATURE_TREE_AY_TYPES_0
cdef object SIGNATURE_TREE_A_QV
Expand Down Expand Up @@ -101,6 +109,10 @@ cdef class Unmarshaller:
)
cdef _read_to_pos(self, unsigned long pos)

cpdef read_boolean(self, object type_)

cdef bint _read_boolean(self)

cpdef read_uint32_unpack(self, object type_)

cdef unsigned int _read_uint32_unpack(self)
Expand Down
62 changes: 51 additions & 11 deletions src/dbus_fast/_private/unmarshaller.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,10 +59,17 @@


SIGNATURE_TREE_EMPTY = get_signature_tree("")
SIGNATURE_TREE_B = get_signature_tree("b")
SIGNATURE_TREE_N = get_signature_tree("n")
SIGNATURE_TREE_S = get_signature_tree("s")
SIGNATURE_TREE_O = get_signature_tree("o")

SIGNATURE_TREE_AY = get_signature_tree("ay")
SIGNATURE_TREE_AS = get_signature_tree("as")
SIGNATURE_TREE_AS_TYPES_0 = SIGNATURE_TREE_AS.types[0]
SIGNATURE_TREE_A_SV = get_signature_tree("a{sv}")
SIGNATURE_TREE_A_SV_TYPES_0 = SIGNATURE_TREE_A_SV.types[0]

SIGNATURE_TREE_AY_TYPES_0 = SIGNATURE_TREE_AY.types[0]
SIGNATURE_TREE_A_QV = get_signature_tree("a{qv}")
SIGNATURE_TREE_A_QV_TYPES_0 = SIGNATURE_TREE_A_QV.types[0]
Expand All @@ -71,6 +78,9 @@
SIGNATURE_TREE_SA_SV_AS_TYPES_1 = SIGNATURE_TREE_SA_SV_AS.types[1]
SIGNATURE_TREE_SA_SV_AS_TYPES_2 = SIGNATURE_TREE_SA_SV_AS.types[2]

SIGNATURE_TREE_OA_SA_SV = get_signature_tree("oa{sa{sv}}")
SIGNATURE_TREE_OA_SA_SV_TYPES_1 = SIGNATURE_TREE_OA_SA_SV.types[1]

HEADER_MESSAGE_ARG_NAME = {
1: "path",
2: "interface",
Expand Down Expand Up @@ -292,6 +302,9 @@ def _read_int16_unpack(self) -> int:
return self._int16_unpack(self._buf, self._pos - INT16_SIZE)[0]

def read_boolean(self, type_: SignatureType) -> bool:
return self._read_boolean()

def _read_boolean(self) -> bool:
return bool(self._read_uint32_unpack())

def read_string_unpack(self, type_: SignatureType) -> str:
Expand Down Expand Up @@ -338,6 +351,22 @@ def _read_variant(self) -> Variant:
self._read_array(SIGNATURE_TREE_A_QV_TYPES_0),
False,
)
elif signature == "s":
return Variant(SIGNATURE_TREE_S, self._read_string_unpack(), False)
elif signature == "b":
return Variant(SIGNATURE_TREE_B, self._read_boolean(), False)
elif signature == "o":
return Variant(SIGNATURE_TREE_O, self._read_string_unpack(), False)
elif signature == "as":
return Variant(
SIGNATURE_TREE_AS, self._read_array(SIGNATURE_TREE_AS_TYPES_0), False
)
elif signature == "a{sv}":
return Variant(
SIGNATURE_TREE_A_SV,
self._read_array(SIGNATURE_TREE_A_SV_TYPES_0),
False,
)
tree = get_signature_tree(signature)
signature_type = tree.types[0]
return Variant(
Expand Down Expand Up @@ -395,20 +424,25 @@ def _read_array(self, type_: SignatureType) -> Iterable[Any]:
# Strings with variant values are the most common case
# so we optimize for that by inlining the string reading
# and the variant reading here
if child_1_token == "v":
if child_0_token in "os":
while self._pos - beginning_pos < array_length:
self._pos += -self._pos & 7 # align 8
key = self._read_string_unpack()
result_dict[key] = self._read_variant()
elif child_0_token == "q":
while self._pos - beginning_pos < array_length:
self._pos += -self._pos & 7 # align 8
key = self._read_uint16_unpack()
result_dict[key] = self._read_variant()
if child_0_token in "os" and child_1_token == "v":
while self._pos - beginning_pos < array_length:
self._pos += -self._pos & 7 # align 8
key = self._read_string_unpack()
result_dict[key] = self._read_variant()
elif child_0_token == "q" and child_1_token == "v":
while self._pos - beginning_pos < array_length:
self._pos += -self._pos & 7 # align 8
key = self._read_uint16_unpack()
result_dict[key] = self._read_variant()
elif child_0_token == "s" and child_1_token == "a":
while self._pos - beginning_pos < array_length:
self._pos += -self._pos & 7 # align 8
key = self._read_string_unpack()
result_dict[key] = self._read_array(child_1)
else:
reader_1 = self._readers[child_1_token]
reader_0 = self._readers[child_0_token]

while self._pos - beginning_pos < array_length:
self._pos += -self._pos & 7 # align 8
key = reader_0(self, child_0)
Expand Down Expand Up @@ -528,6 +562,12 @@ def _read_body(self) -> None:
self._read_array(SIGNATURE_TREE_SA_SV_AS_TYPES_1),
self._read_array(SIGNATURE_TREE_SA_SV_AS_TYPES_2),
]
elif signature == "oa{sa{sv}}":
tree = SIGNATURE_TREE_OA_SA_SV
body = [
self._read_string_unpack(),
self._read_array(SIGNATURE_TREE_OA_SA_SV_TYPES_1),
]
else:
tree = get_signature_tree(signature)
body = [self._readers[t.token](self, t) for t in tree.types]
Expand Down
93 changes: 93 additions & 0 deletions tests/test_marshaller.py
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,99 @@ def test_unmarshall_bluez_message():
]


def test_unmarshall_bluez_interfaces_added_message():
bluez_interfaces_added_message = (
b'l\4\1\1\240\2\0\0\227\272\23\0u\0\0\0\1\1o\0\1\0\0\0/\0\0\0\0\0\0\0\2\1s\0"\0\0\0'
b"org.freedesktop.DBus.ObjectManager\0\0\0\0\0\0\3\1s\0\17\0\0\0InterfacesAdded\0\10"
b"\1g\0\noa{sa{sv}}\0\7\1s\0\4\0\0\0:1.4\0\0\0\0%\0\0\0/org/bluez/hci1/dev_58_2D_34"
b"_60_26_36\0\0\0p\2\0\0#\0\0\0org.freedesktop.DBus.Introspectable\0\0\0\0\0\0\0\0\0"
b"\21\0\0\0org.bluez.Device1\0\0\0\364\1\0\0\0\0\0\0\7\0\0\0Address\0\1s\0\0\21\0\0"
b"\00058:2D:34:60:26:36\0\0\0\v\0\0\0AddressType\0\1s\0\0\6\0\0\0public\0\0\4\0\0\0"
b"Name\0\1s\0\33\0\0\0Qingping Door/Window Sensor\0\0\0\0\0\5\0\0\0Alias\0\1s\0\0\0"
b"\0\33\0\0\0Qingping Door/Window Sensor\0\6\0\0\0Paired\0\1b\0\0\0\0\0\0\0\0\0\0\0"
b"\7\0\0\0Trusted\0\1b\0\0\0\0\0\0\0\0\0\0\7\0\0\0Blocked\0\1b\0\0\0\0\0\0\0\0\0\0\r"
b"\0\0\0LegacyPairing\0\1b\0\0\0\0\0\0\0\0\0\0\0\0\4\0\0\0RSSI\0\1n\0\316\377\0\0\t"
b"\0\0\0Connected\0\1b\0\0\0\0\0\0\0\0\5\0\0\0UUIDs\0\2as\0\0\0\0\0\0\0\0\0\0\0\7\0"
b"\0\0Adapter\0\1o\0\0\17\0\0\0/org/bluez/hci1\0\0\0\0\0\v\0\0\0ServiceData\0\5a{sv}"
b"\0\0@\0\0\0\0\0\0\0$\0\0\0000000fe95-0000-1000-8000-00805f9b34fb\0\2ay\0\0\0\0\f\0"
b"\0\0000X\326\3\0026&`4-X\10\20\0\0\0ServicesResolved\0\1b\0\0\0\0\0\0\0\0\0\37\0\0"
b"\0org.freedesktop.DBus.Properties\0\0\0\0\0"
)

stream = io.BytesIO(bluez_interfaces_added_message)
unmarshaller = Unmarshaller(stream)
assert unmarshaller.unmarshall()
message = unmarshaller.message
assert message is not None
assert message.body == [
"/org/bluez/hci1/dev_58_2D_34_60_26_36",
{
"org.bluez.Device1": {
"Adapter": Variant("o", "/org/bluez/hci1"),
"Address": Variant("s", "58:2D:34:60:26:36"),
"AddressType": Variant("s", "public"),
"Alias": Variant("s", "Qingping Door/Window Sensor"),
"Blocked": Variant("b", False),
"Connected": Variant("b", False),
"LegacyPairing": Variant("b", False),
"Name": Variant("s", "Qingping Door/Window Sensor"),
"Paired": Variant("b", False),
"RSSI": Variant("n", -50),
"ServiceData": Variant(
"a{sv}",
{
"0000fe95-0000-1000-8000-00805f9b34fb": Variant(
"ay", bytearray(b"0X\xd6\x03\x026&`4-X\x08")
)
},
),
"ServicesResolved": Variant("b", False),
"Trusted": Variant("b", False),
"UUIDs": Variant("as", []),
},
"org.freedesktop.DBus.Introspectable": {},
"org.freedesktop.DBus.Properties": {},
},
]
assert message.sender == ":1.4"
assert message.path == "/"
assert message.interface == "org.freedesktop.DBus.ObjectManager"
assert message.member == "InterfacesAdded"
assert message.signature == "oa{sa{sv}}"
assert message.message_type == MessageType.SIGNAL
assert message.flags == MessageFlag.NO_REPLY_EXPECTED
assert message.serial == 1292951
assert message.destination is None
unpacked = unpack_variants(message.body)
assert unpacked == [
"/org/bluez/hci1/dev_58_2D_34_60_26_36",
{
"org.bluez.Device1": {
"Adapter": "/org/bluez/hci1",
"Address": "58:2D:34:60:26:36",
"AddressType": "public",
"Alias": "Qingping Door/Window Sensor",
"Blocked": False,
"Connected": False,
"LegacyPairing": False,
"Name": "Qingping Door/Window Sensor",
"Paired": False,
"RSSI": -50,
"ServiceData": {
"0000fe95-0000-1000-8000-00805f9b34fb": bytearray(
b"0X\xd6\x03" b"\x026&`" b"4-X\x08"
)
},
"ServicesResolved": False,
"Trusted": False,
"UUIDs": [],
},
"org.freedesktop.DBus.Introspectable": {},
"org.freedesktop.DBus.Properties": {},
},
]


def test_ay_buffer():
body = [bytes(10000)]
msg = Message(path="/test", member="test", signature="ay", body=body)
Expand Down

0 comments on commit c05a27a

Please sign in to comment.