8000 feat: speed up parsing incoming records (#1458) · python-zeroconf/python-zeroconf@783c1b3 · GitHub
[go: up one dir, main page]

Skip to content

Commit 783c1b3

Browse files
authored
feat: speed up parsing incoming records (#1458)
1 parent 9f6af54 commit 783c1b3

File tree

4 files changed

+129
-26
lines changed

4 files changed

+129
-26
lines changed

src/zeroconf/_dns.pxd

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,21 +30,25 @@ cdef class DNSEntry:
3030
cdef public cython.uint class_
3131
cdef public bint unique
3232

33-
cdef _set_class(self, cython.uint class_)
33+
cdef _fast_init_entry(self, str name, cython.uint type_, cython.uint class_)
3434

3535
cdef bint _dns_entry_matches(self, DNSEntry other)
3636

3737
cdef class DNSQuestion(DNSEntry):
3838

3939
cdef public cython.int _hash
4040

41+
cdef _fast_init(self, str name, cython.uint type_, cython.uint class_)
42+
4143
cpdef bint answered_by(self, DNSRecord rec)
4244

4345
cdef class DNSRecord(DNSEntry):
4446

4547
cdef public cython.float ttl
4648
cdef public double created
4749

50+
cdef _fast_init_record(self, str name, cython.uint type_, cython.uint class_, cython.float ttl, double created)
51+
4852
cdef bint _suppressed_by_answer(self, DNSRecord answer)
4953

5054
@cython.locals(
@@ -69,9 +73,11 @@ cdef class DNSRecord(DNSEntry):
6973
cdef class DNSAddress(DNSRecord):
7074

7175
cdef public cython.int _hash
72-
cdef public object address
76+
cdef public bytes address
7377
cdef public object scope_id
7478

79+
cdef _fast_init(self, str name, cython.uint type_, cython.uint class_, cython.float ttl, bytes address, object scope_id, double created)
80+
7581
cdef bint _eq(self, DNSAddress other)
7682

7783
cpdef write(self, DNSOutgoing out)
@@ -83,6 +89,8 @@ cdef class DNSHinfo(DNSRecord):
8389
cdef public str cpu
8490
cdef public str os
8591

92+
cdef _fast_init(self, str name, cython.uint type_, cython.uint class_, cython.float ttl, str cpu, str os, double created)
93+
8694
cdef bint _eq(self, DNSHinfo other)
8795

8896
cpdef write(self, DNSOutgoing out)
@@ -93,6 +101,8 @@ cdef class DNSPointer(DNSRecord):
93101
cdef public str alias
94102
cdef public str alias_key
95103

104+
cdef _fast_init(self, str name, cython.uint type_, cython.uint class_, cython.float ttl, str alias, double created)
105+
96106
cdef bint _eq(self, DNSPointer other)
97107

98108
cpdef write(self, DNSOutgoing out)
@@ -102,6 +112,8 @@ cdef class DNSText(DNSRecord):
102112
cdef public cython.int _hash
103113
cdef public bytes text
104114

115+
cdef _fast_init(self, str name, cython.uint type_, cython.uint class_, cython.float ttl, bytes text, double created)
116+
105117
cdef bint _eq(self, DNSText other)
106118

107119
cpdef write(self, DNSOutgoing out)
@@ -115,6 +127,8 @@ cdef class DNSService(DNSRecord):
115127
cdef public str server
116128
cdef public str server_key
117129

130+
cdef _fast_init(self, str name, cython.uint type_, cython.uint class_, cython.float ttl, cython.uint priority, cython.uint weight, cython.uint port, str server, double created)
131+
118132
cdef bint _eq(self, DNSService other)
119133

120134
cpdef write(self, DNSOutgoing out)
@@ -125,6 +139,8 @@ cdef class DNSNsec(DNSRecord):
125139
cdef public object next_name
126140
cdef public cython.list rdtypes
127141

142+
cdef _fast_init(self, str name, cython.uint type_, cython.uint class_, cython.float ttl, str next_name, cython.list rdtypes, double created)
143+
128144
cdef bint _eq(self, DNSNsec other)
129145

130146
cpdef write(self, DNSOutgoing out)

src/zeroconf/_dns.py

Lines changed: 79 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -67,12 +67,13 @@ class DNSEntry:
6767
__slots__ = ("class_", "key", "name", "type", "unique")
6868

6969
def __init__(self, name: str, type_: int, class_: int) -> None:
70+
self._fast_init_entry(name, type_, class_)
71+
72+
def _fast_init_entry(self, name: str, type_: _int, class_: _int) -> None:
73+
"""Fast init for reuse."""
7074
self.name = name
7175
self.key = name.lower()
7276
self.type = type_
73-
self._set_class(class_)
74-
75-
def _set_class(self, class_: _int) -> None:
7677
self.class_ = class_ & _CLASS_MASK
7778
self.unique = (class_ & _CLASS_UNIQUE) != 0
7879

@@ -111,7 +112,11 @@ class DNSQuestion(DNSEntry):
111112
__slots__ = ("_hash",)
112113

113114
def __init__(self, name: str, type_: int, class_: int) -> None:
114-
super().__init__(name, type_, class_)
115+
self._fast_init(name, type_, class_)
116+
117+
def _fast_init(self, name: str, type_: _int, class_: _int) -> None:
118+
"""Fast init for reuse."""
119+
self._fast_init_entry(name, type_, class_)
115120
self._hash = hash((self.key, type_, self.class_))
116121

117122
def answered_by(self, rec: "DNSRecord") -> bool:
@@ -168,9 +173,13 @@ def __init__(
168173
ttl: Union[float, int],
169174
created: Optional[float] = None,
170175
) -> None:
171-
super().__init__(name, type_, class_)
176+
self._fast_init_record(name, type_, class_, ttl, created or current_time_millis())
177+
178+
def _fast_init_record(self, name: str, type_: _int, class_: _int, ttl: _float, created: _float) -> None:
179+
"""Fast init for reuse."""
180+
self._fast_init_entry(name, type_, class_)
172181
self.ttl = ttl
173-
self.created = created or current_time_millis()
182+
self.created = created
174183

175184
def __eq__(self, other: Any) -> bool: # pylint: disable=no-self-use
176185
"""Abstract method"""
@@ -248,7 +257,20 @@ def __init__(
248257
scope_id: Optional[int] = None,
249258
created: Optional[float] = None,
250259
) -> None:
251-
super().__init__(name, type_, class_, ttl, created)
260+
self._fast_init(name, type_, class_, ttl, address, scope_id, created or current_time_millis())
261+
262+
def _fast_init(
263+
self,
264+
name: str,
265+
type_: _int,
266+
class_: _int,
267+
ttl: _float,
268+ address: bytes,
269+
scope_id: Optional[_int],
270+
created: _float,
271+
) -> None:
272+
"""Fast init for reuse."""
273+
self._fast_init_record(name, type_, class_, ttl, created)
252274
self.address = address
253275
self.scope_id = scope_id
254276
self._hash = hash((self.key, type_, self.class_, address, scope_id))
@@ -300,7 +322,13 @@ def __init__(
300322
os: str,
301323
created: Optional[float] = None,
302324
) -> None:
303-
super().__init__(name, type_, class_, ttl, created)
325+
self._fast_init(name, type_, class_, ttl, cpu, os, created or current_time_millis())
326+
327+
def _fast_init(
328+
self, name: str, type_: _int, class_: _int, ttl: _float, cpu: str, os: str, created: _float
329+
) -> None:
330+
"""Fast init for reuse."""
331+
self._fast_init_record(name, type_, class_, ttl, created)
304332
self.cpu = cpu
305333
self.os = os
306334
self._hash = hash((self.key, type_, self.class_, cpu, os))
@@ -341,7 +369,12 @@ def __init__(
341369
alias: str,
342370
created: Optional[float] = None,
343371
) -> None:
344-
super().__init__(name, type_, class_, ttl, created)
372+
self._fast_init(name, type_, class_, ttl, alias, created or current_time_millis())
373+
374+
def _fast_init(
375+
self, name: str, type_: _int, class_: _int, ttl: _float, alias: str, created: _float
376+
) -> None:
377+
self._fast_init_record(name, type_, class_, ttl, created)
345378
self.alias = alias
346379
self.alias_key = alias.lower()
347380
self._hash = hash((self.key, type_, self.class_, self.alias_key))
@@ -391,7 +424,12 @@ def __init__(
391424
text: bytes,
392425
created: Optional[float] = None,
393426
) -> None:
394-
super().__init__(name, type_, class_, ttl, created)
427+
self._fast_init(name, type_, class_, ttl, text, created or current_time_millis())
428+
429+
def _fast_init(
430+
self, name: str, type_: _int, class_: _int, ttl: _float, text: bytes, created: _float
431+
) -> None:
432+
self._fast_init_record(name, type_, class_, ttl, created)
395433
self.text = text
396434
self._hash = hash((self.key, type_, self.class_, text))
397435

@@ -435,7 +473,23 @@ def __init__(
435473
server: str,
436474
created: Optional[float] = None,
437475
) -> None:
438-
super().__init__(name, type_, class_, ttl, created)
476+
self._fast_init(
477+
name, type_, class_, ttl, priority, weight, port, server, created or current_time_millis()
478+
)
479+
480+
def _fast_init(
481+
self,
482+
name: str,
483+
type_: _int,
484+
class_: _int,
485+
ttl: _float,
486+
priority: _int,
487+
weight: _int,
488+
port: _int,
489+
server: str,
490+
created: _float,
491+
) -> None:
492+
self._fast_init_record(name, type_, class_, ttl, created)
439493
self.priority = priority
440494
self.weight = weight
441495
self.port = port
@@ -483,12 +537,24 @@ def __init__(
483537
name: str,
484538
type_: int,
485539
class_: int,
486-
ttl: int,
540+
ttl: Union[int, float],
487541
next_name: str,
488542
rdtypes: List[int],
489543
created: Optional[float] = None,
490544
) -> None:
491-
super().__init__(name, type_, class_, ttl, created)
545+
self._fast_init(name, type_, class_, ttl, next_name, rdtypes, created or current_time_millis())
546+
547+
def _fast_init(
548+
self,
549+
name: str,
550+
type_: _int,
551+
class_: _int,
552+
ttl: _float,
553+
next_name: str,
554+
rdtypes: List[_int],
555+
created: _float,
556+
) -> None:
557+
self._fast_init_record(name, type_, class_, ttl, created)
492558
self.next_name = next_name
493559
self.rdtypes = sorted(rdtypes)
494560
self._hash = hash((self.key, type_, self.class_, next_name, *self.rdtypes))

src/zeroconf/_protocol/incoming.pxd

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ cdef class DNSIncoming:
9797
)
9898
cdef void _read_others(self)
9999

100-
@cython.locals(offset="unsigned int")
100+
@cython.locals(offset="unsigned int", question=DNSQuestion)
101101
cdef _read_questions(self)
102102

103103
@cython.locals(
@@ -109,9 +109,15 @@ cdef class DNSIncoming:
109109

110110
@cython.locals(
111111
name_start="unsigned int",
112-
offset="unsigned int"
112+
offset="unsigned int",
113+
address_rec=DNSAddress,
114+
pointer_rec=DNSPointer,
115+
text_rec=DNSText,
116+
srv_rec=DNSService,
117+
hinfo_rec=DNSHinfo,
118+
nsec_rec=DNSNsec,
113119
)
114-
cdef _read_record(self, object domain, unsigned int type_, unsigned int class_, unsigned int ttl, unsigned int length)
120+
cdef _read_record(self, str domain, unsigned int type_, unsigned int class_, unsigned int ttl, unsigned int length)
115121

116122
@cython.locals(
117123
offset="unsigned int",

src/zeroconf/_protocol/incoming.py

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -246,7 +246,8 @@ def _read_questions(self) -> None:
246246
# The question has 2 unsigned shorts in network order
247247
type_ = view[offset] << 8 | view[offset + 1]
248248
class_ = view[offset + 2] << 8 | view[offset + 3]
249-
question = DNSQuestion(name, type_, class_)
249+
question = DNSQuestion.__new__(DNSQuestion)
250+
question._fast_init(name, type_, class_)
250251
if question.unique: # QU questions use the same bit as unique
251252
self._has_qu_question = True
252253
questions.append(question)
@@ -306,11 +307,17 @@ def _read_record(
306307
) -> Optional[DNSRecord]:
307308
"""Read known records types and skip unknown ones."""
308309
if type_ == _TYPE_A:
309-
return DNSAddress(domain, type_, class_, ttl, self._read_string(4), None, self.now)
310+
address_rec = DNSAddress.__new__(DNSAddress)
311+
address_rec._fast_init(domain, type_, class_, ttl, self._read_string(4), None, self.now)
312+
return address_rec
310313
if type_ in (_TYPE_CNAME, _TYPE_PTR):
311-
return DNSPointer(domain, type_, class_, ttl, self._read_name(), self.now)
314+
pointer_rec = DNSPointer.__new__(DNSPointer)
315+
pointer_rec._fast_init(domain, type_, class_, ttl, self._read_name(), self.now)
316+
return pointer_rec
312317
if type_ == _TYPE_TXT:
313-
return DNSText(domain, type_, class_, ttl, self._read_string(length), self.now)
318+
text_rec = DNSText.__new__(DNSText)
319+
text_rec._fast_init(domain, type_, class_, ttl, self._read_string(length), self.now)
320+
return text_rec
314321
if type_ == _TYPE_SRV:
315322
view = self.view
316323
offset = self.offset
@@ -319,7 +326,8 @@ def _read_record(
319326
priority = view[offset] << 8 | view[offset + 1]
320327
weight = view[offset + 2] << 8 | view[offset + 3]
321328
port = view[offset + 4] << 8 | view[offset + 5]
322-
return DNSService(
329+
srv_rec = DNSService.__new__(DNSService)
330+
srv_rec._fast_init(
323331
domain,
324332
type_,
325333
class_,
@@ -330,8 +338,10 @@ def _read_record(
330338
self._read_name(),
331339
self.now,
332340
)
341+
return srv_rec
333342
if type_ == _TYPE_HINFO:
334-
return DNSHinfo(
343+
hinfo_rec = DNSHinfo.__new__(DNSHinfo)
344+
hinfo_rec._fast_init(
335345
domain,
336346
type_,
337347
class_,
@@ -340,8 +350,10 @@ def _read_record(
340350
self._read_character_string(),
341351
self.now,
342352
)
353+
return hinfo_rec
343354
if type_ == _TYPE_AAAA:
344-
return DNSAddress(
355+
address_rec = DNSAddress.__new__(DNSAddress)
356+
address_rec._fast_init(
345357
domain,
346358
type_,
347359
class_,
@@ -350,9 +362,11 @@ def _read_record(
350362
self.scope_id,
351363
self.now,
352364
)
365+
return address_rec
353366
if type_ == _TYPE_NSEC:
354367
name_start = self.offset
355-
return DNSNsec(
368+
nsec_rec = DNSNsec.__new__(DNSNsec)
369+
nsec_rec._fast_init(
356370
domain,
357371
type_,
358372
class_,
@@ -361,6 +375,7 @@ def _read_record(
361375
self._read_bitmap(name_start + length),
362376
self.now,
363377
)
378+
return nsec_rec
364379
# Try to ignore types we don't know about
365380
# Skip the payload for the resource record so the next
366381
# records can be parsed correctly

0 commit comments

Comments
 (0)
0