Skip to content

Commit bf2f366

Browse files
authored
feat: significantly speed up writing outgoing dns records (#1260)
1 parent 72bd07d commit bf2f366

2 files changed

Lines changed: 39 additions & 15 deletions

File tree

src/zeroconf/_protocol/outgoing.pxd

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,18 @@ cdef cython.uint _MAX_MSG_TYPICAL
1717

1818
cdef object TYPE_CHECKING
1919

20+
cdef object PACK_BYTE
21+
cdef object PACK_SHORT
22+
cdef object PACK_LONG
23+
2024
cdef class DNSOutgoing:
2125

2226
cdef public unsigned int flags
2327
cdef public object finished
2428
cdef public object id
2529
cdef public bint multicast
2630
cdef public cython.list packets_data
27-
cdef public object names
31+
cdef public cython.dict names
2832
cdef public cython.list data
2933
cdef public unsigned int size
3034
cdef public object allow_long
@@ -53,24 +57,32 @@ cdef class DNSOutgoing:
5357
)
5458
cdef _write_record(self, DNSRecord record, object now)
5559

56-
cdef _write_record_class(self, object record)
60+
cdef _write_record_class(self, DNSEntry record)
5761

5862
cdef _check_data_limit_or_rollback(self, object start_data_length, object start_size)
5963

6064
cdef _write_questions_from_offset(self, object questions_offset)
6165

6266
cdef _write_answers_from_offset(self, object answer_offset)
6367

64-
cdef _write_records_from_offset(self, object records, object offset)
68+
cdef _write_records_from_offset(self, cython.list records, object offset)
6569

6670
cdef _has_more_to_add(self, object questions_offset, object answer_offset, object authority_offset, object additional_offset)
6771

6872
cdef _write_ttl(self, DNSRecord record, object now)
6973

70-
cpdef write_name(self, object name)
74+
@cython.locals(
75+
labels=cython.list,
76+
label=cython.str,
77+
)
78+
cpdef write_name(self, cython.str name)
7179

7280
cpdef write_short(self, object value)
7381

82+
cpdef write_string(self, cython.bytes value)
83+
84+
cpdef _write_utf(self, cython.str value)
85+
7486
@cython.locals(
7587
questions_offset=object,
7688
answer_offset=object,

src/zeroconf/_protocol/outgoing.py

Lines changed: 23 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222

2323
import enum
2424
import logging
25+
from struct import Struct
2526
from typing import TYPE_CHECKING, Dict, List, Optional, Sequence, Tuple, Union
2627

2728
from .._cache import DNSCache
@@ -43,10 +44,16 @@
4344
str_ = str
4445
float_ = float
4546
int_ = int
47+
bytes_ = bytes
4648
DNSQuestion_ = DNSQuestion
4749
DNSRecord_ = DNSRecord
4850

4951

52+
PACK_BYTE = Struct('>B').pack
53+
PACK_SHORT = Struct('>H').pack
54+
PACK_LONG = Struct('>L').pack
55+
56+
5057
class State(enum.Enum):
5158
init = 0
5259
finished = 1
@@ -200,35 +207,35 @@ def add_question_or_all_cache(
200207

201208
def _write_byte(self, value: int_) -> None:
202209
"""Writes a single byte to the packet"""
203-
self.data.append(value.to_bytes(1, 'big'))
210+
self.data.append(PACK_BYTE(value))
204211
self.size += 1
205212

206213
def _insert_short_at_start(self, value: int_) -> None:
207214
"""Inserts an unsigned short at the start of the packet"""
208-
self.data.insert(0, value.to_bytes(2, 'big'))
215+
self.data.insert(0, PACK_SHORT(value))
209216

210217
def _replace_short(self, index: int_, value: int_) -> None:
211218
"""Replaces an unsigned short in a certain position in the packet"""
212-
self.data[index] = value.to_bytes(2, 'big')
219+
self.data[index] = PACK_SHORT(value)
213220

214221
def write_short(self, value: int_) -> None:
215222
"""Writes an unsigned short to the packet"""
216-
self.data.append(value.to_bytes(2, 'big'))
223+
self.data.append(PACK_SHORT(value))
217224
self.size += 2
218225

219226
def _write_int(self, value: Union[float, int]) -> None:
220227
"""Writes an unsigned integer to the packet"""
221-
self.data.append(int(value).to_bytes(4, 'big'))
228+
self.data.append(PACK_LONG(int(value)))
222229
self.size += 4
223230

224-
def write_string(self, value: bytes) -> None:
231+
def write_string(self, value: bytes_) -> None:
225232
"""Writes a string to the packet"""
226233
if TYPE_CHECKING:
227234
assert isinstance(value, bytes)
228235
self.data.append(value)
229236
self.size += len(value)
230237

231-
def _write_utf(self, s: str) -> None:
238+
def _write_utf(self, s: str_) -> None:
232239
"""Writes a UTF-8 string of a given length to the packet"""
233240
utfstr = s.encode('utf-8')
234241
length = len(utfstr)
@@ -446,7 +453,8 @@ def _packets(self) -> List[bytes]:
446453
questions_offset, answer_offset, authority_offset, additional_offset
447454
):
448455
# https://datatracker.ietf.org/doc/html/rfc6762#section-7.2
449-
log.debug("Setting TC flag")
456+
if debug_enable: # pragma: no branch
457+
log.debug("Setting TC flag")
450458
self._insert_short_at_start(self.flags | _FLAGS_TC)
451459
else:
452460
self._insert_short_at_start(self.flags)
@@ -459,9 +467,13 @@ def _packets(self) -> List[bytes]:
459467
self.packets_data.append(b''.join(self.data))
460468
self._reset_for_next_packet()
461469

462-
if (questions_written + answers_written + authorities_written + additionals_written) == 0 and (
463-
len(self.questions) + len(self.answers) + len(self.authorities) + len(self.additionals)
464-
) > 0:
470+
if (
471+
not questions_written
472+
and not answers_written
473+
and not authorities_written
474+
and not additionals_written
475+
and (self.questions or self.answers or self.authorities or self.additionals)
476+
):
465477
log.warning("packets() made no progress adding records; returning")
466478
break
467479
self.state = State.finished

0 commit comments

Comments
 (0)