Skip to content

Commit 9caeabb

Browse files
authored
feat: speed up writing name compression for outgoing packets (#1312)
1 parent cfa1fd6 commit 9caeabb

3 files changed

Lines changed: 47 additions & 33 deletions

File tree

bench/outgoing.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,7 @@ def generate_packets() -> DNSOutgoing:
158158

159159

160160
def make_outgoing_message() -> None:
161-
out.state = State.init
161+
out.state = State.init.value
162162
out.finished = False
163163
out.packets()
164164

src/zeroconf/_protocol/outgoing.pxd

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,8 @@ cdef object PACK_BYTE
2121
cdef object PACK_SHORT
2222
cdef object PACK_LONG
2323

24-
cdef object STATE_INIT
25-
cdef object STATE_FINISHED
24+
cdef unsigned int STATE_INIT
25+
cdef unsigned int STATE_FINISHED
2626

2727
cdef object LOGGING_IS_ENABLED_FOR
2828
cdef object LOGGING_DEBUG
@@ -40,7 +40,7 @@ cdef class DNSOutgoing:
4040
cdef public cython.list data
4141
cdef public unsigned int size
4242
cdef public bint allow_long
43-
cdef public object state
43+
cdef public unsigned int state
4444
cdef public cython.list questions
4545
cdef public cython.list answers
4646
cdef public cython.list authorities
@@ -91,13 +91,17 @@ cdef class DNSOutgoing:
9191
)
9292
cpdef write_name(self, cython.str name)
9393

94+
cdef _write_link_to_name(self, unsigned int index)
95+
9496
cpdef write_short(self, object value)
9597

9698
cpdef write_string(self, cython.bytes value)
9799

98100
cpdef _write_utf(self, cython.str value)
99101

100102
@cython.locals(
103+
debug_enable=bint,
104+
made_progress=bint,
101105
questions_offset=object,
102106
answer_offset=object,
103107
authority_offset=object,
@@ -107,7 +111,7 @@ cdef class DNSOutgoing:
107111
authorities_written=object,
108112
additionals_written=object,
109113
)
110-
cdef _packets(self)
114+
cpdef packets(self)
111115

112116
cpdef add_question_or_all_cache(self, DNSCache cache, object now, str name, object type_, object class_)
113117

@@ -124,6 +128,6 @@ cdef class DNSOutgoing:
124128

125129
cpdef add_additional_answer(self, DNSRecord record)
126130

127-
cpdef is_query(self)
131+
cpdef bint is_query(self)
128132

129-
cpdef is_response(self)
133+
cpdef bint is_response(self)

src/zeroconf/_protocol/outgoing.py

Lines changed: 36 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -61,8 +61,8 @@ class State(enum.Enum):
6161
finished = 1
6262

6363

64-
STATE_INIT = State.init
65-
STATE_FINISHED = State.finished
64+
STATE_INIT = State.init.value
65+
STATE_FINISHED = State.finished.value
6666

6767
LOGGING_IS_ENABLED_FOR = log.isEnabledFor
6868
LOGGING_DEBUG = logging.DEBUG
@@ -277,30 +277,41 @@ def write_name(self, name: str_) -> None:
277277
"""
278278

279279
# split name into each label
280-
name_length = 0
281280
if name.endswith('.'):
282-
name = name[: len(name) - 1]
283-
labels = name.split('.')
284-
# Write each new label or a pointer to the existing
285-
# on in the packet
281+
name = name[:-1]
282+
283+
index = self.names.get(name, 0)
284+
if index:
285+
self._write_link_to_name(index)
286+
return
287+
286288
start_size = self.size
287-
for count in range(len(labels)):
288-
label = name if count == 0 else '.'.join(labels[count:])
289-
index = self.names.get(label, 0)
289+
labels = name.split('.')
290+
# Write each new label or a pointer to the existing one in the packet
291+
self.names[name] = start_size
292+
self._write_utf(labels[0])
293+
294+
name_length = 0
295+
for count in range(1, len(labels)):
296+
partial_name = '.'.join(labels[count:])
297+
index = self.names.get(partial_name, 0)
290298
if index:
291-
# If part of the name already exists in the packet,
292-
# create a pointer to it
293-
self._write_byte((index >> 8) | 0xC0)
294-
self._write_byte(index & 0xFF)
299+
self._write_link_to_name(index)
295300
return
296301
if name_length == 0:
297302
name_length = len(name.encode('utf-8'))
298-
self.names[label] = start_size + name_length - len(label.encode('utf-8'))
303+
self.names[partial_name] = start_size + name_length - len(partial_name.encode('utf-8'))
299304
self._write_utf(labels[count])
300305

301306
# this is the end of a name
302307
self._write_byte(0)
303308

309+
def _write_link_to_name(self, index: int_) -> None:
310+
# If part of the name already exists in the packet,
311+
# create a pointer to it
312+
self._write_byte((index >> 8) | 0xC0)
313+
self._write_byte(index & 0xFF)
314+
304315
def _write_question(self, question: DNSQuestion_) -> bool:
305316
"""Writes a question to the packet"""
306317
start_data_length = len(self.data)
@@ -406,9 +417,6 @@ def packets(self) -> List[bytes]:
406417
will be written out to a single oversized packet no more than
407418
_MAX_MSG_ABSOLUTE in length (and hence will be subject to IP
408419
fragmentation potentially)."""
409-
return self._packets()
410-
411-
def _packets(self) -> List[bytes]:
412420
if self.state == STATE_FINISHED:
413421
return self.packets_data
414422

@@ -445,6 +453,8 @@ def _packets(self) -> List[bytes]:
445453
authorities_written = self._write_records_from_offset(self.authorities, authority_offset)
446454
additionals_written = self._write_records_from_offset(self.additionals, additional_offset)
447455

456+
made_progress = bool(self.data)
457+
448458
self._insert_short_at_start(additionals_written)
449459
self._insert_short_at_start(authorities_written)
450460
self._insert_short_at_start(answers_written)
@@ -479,16 +489,16 @@ def _packets(self) -> List[bytes]:
479489
self._insert_short_at_start(self.id)
480490

481491
self.packets_data.append(b''.join(self.data))
482-
self._reset_for_next_packet()
483492

484-
if (
485-
not questions_written
486-
and not answers_written
487-
and not authorities_written
488-
and not additionals_written
489-
and (self.questions or self.answers or self.authorities or self.additionals)
490-
):
493+
if not made_progress:
494+
# Generating an empty packet is not a desirable outcome, but currently
495+
# too many internals rely on this behavior. So, we'll just return an
496+
# empty packet and log a warning until this can be refactored at a later
497+
# date.
491498
log.warning("packets() made no progress adding records; returning")
492499
break
500+
501+
self._reset_for_next_packet()
502+
493503
self.state = STATE_FINISHED
494504
return self.packets_data

0 commit comments

Comments
 (0)