Skip to content

Commit 99a6f98

Browse files
authored
feat: speed up unpacking text records in ServiceInfo (#1212)
1 parent 32a016e commit 99a6f98

1 file changed

Lines changed: 27 additions & 9 deletions

File tree

src/zeroconf/_services/info.py

Lines changed: 27 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -172,7 +172,7 @@ def __init__(
172172
self.priority = priority
173173
self.server = server if server else None
174174
self.server_key = server.lower() if server else None
175-
self._properties: Dict[Union[str, bytes], Optional[Union[str, bytes]]] = {}
175+
self._properties: Optional[Dict[Union[str, bytes], Optional[Union[str, bytes]]]] = None
176176
if isinstance(properties, bytes):
177177
self._set_text(properties)
178178
else:
@@ -226,14 +226,18 @@ def addresses(self, value: List[bytes]) -> None:
226226
self._ipv6_addresses.append(addr)
227227

228228
@property
229-
def properties(self) -> Dict:
229+
def properties(self) -> Dict[Union[str, bytes], Optional[Union[str, bytes]]]:
230230
"""If properties were set in the constructor this property returns the original dictionary
231231
of type `Dict[Union[bytes, str], Any]`.
232232
233233
If properties are coming from the network, after decoding a TXT record, the keys are always
234234
bytes and the values are either bytes, if there was a value, even empty, or `None`, if there
235235
was none. No further decoding is attempted. The type returned is `Dict[bytes, Optional[bytes]]`.
236236
"""
237+
if self._properties is None:
238+
self._unpack_text_into_properties()
239+
if TYPE_CHECKING:
240+
assert self._properties is not None
237241
return self._properties
238242

239243
async def async_wait(self, timeout: float) -> None:
@@ -317,10 +321,10 @@ def parsed_scoped_addresses(self, version: IPVersion = IPVersion.All) -> List[st
317321
for addr in self._ip_addresses_by_version_value(version.value)
318322
]
319323

320-
def _set_properties(self, properties: Dict) -> None:
324+
def _set_properties(self, properties: Dict[Union[str, bytes], Optional[Union[str, bytes]]]) -> None:
321325
"""Sets properties and text of this info from a dictionary"""
322326
self._properties = properties
323-
list_ = []
327+
list_: List[bytes] = []
324328
result = b''
325329
for key, value in properties.items():
326330
if isinstance(key, str):
@@ -338,14 +342,25 @@ def _set_properties(self, properties: Dict) -> None:
338342

339343
def _set_text(self, text: bytes) -> None:
340344
"""Sets properties and text given a text field"""
345+
if text == self.text:
346+
return
341347
self.text = text
348+
# Clear the properties cache
349+
self._properties = None
350+
351+
def _unpack_text_into_properties(self) -> None:
352+
"""Unpacks the text field into properties"""
353+
text = self.text
342354
end = len(text)
343355
if end == 0:
356+
# Properties should be set atomically
357+
# in case another thread is reading them
344358
self._properties = {}
345359
return
360+
346361
result: Dict[Union[str, bytes], Optional[Union[str, bytes]]] = {}
347362
index = 0
348-
strs = []
363+
strs: List[bytes] = []
349364
while index < end:
350365
length = text[index]
351366
index += 1
@@ -355,17 +370,20 @@ def _set_text(self, text: bytes) -> None:
355370
key: bytes
356371
value: Optional[bytes]
357372
for s in strs:
358-
try:
359-
key, value = s.split(b'=', 1)
360-
except ValueError:
373+
key_value = s.split(b'=', 1)
374+
if len(key_value) == 2:
375+
key, value = key_value
376+
else:
361377
# No equals sign at all
362378
key = s
363379
value = None
364380

365381
# Only update non-existent properties
366-
if key and result.get(key) is None:
382+
if key and key not in result:
367383
result[key] = value
368384

385+
# Properties should be set atomically
386+
# in case another thread is reading them
369387
self._properties = result
370388

371389
def get_name(self) -> str:

0 commit comments

Comments
 (0)