Skip to content

Commit

Permalink
feat: optimize equality checks for DNS records (#1120)
Browse files Browse the repository at this point in the history
  • Loading branch information
bdraco committed Dec 22, 2022
1 parent 255a884 commit 3a25ff7
Show file tree
Hide file tree
Showing 2 changed files with 104 additions and 72 deletions.
82 changes: 53 additions & 29 deletions src/zeroconf/_dns.pxd
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@

import cython


cdef object _LEN_BYTE
Expand All @@ -12,67 +13,90 @@ cdef object _EXPIRE_FULL_TIME_MS
cdef object _EXPIRE_STALE_TIME_MS
cdef object _RECENT_TIME_MS

cdef object _CLASS_UNIQUE
cdef object _CLASS_MASK

cdef class DNSEntry:

cdef public key
cdef public name
cdef public type
cdef public class_
cdef public unique
cdef public object key
cdef public object name
cdef public object type
cdef public object class_
cdef public object unique

cdef _dns_entry_matches(self, DNSEntry other)

cdef class DNSQuestion(DNSEntry):

cdef public _hash
cdef public cython.int _hash

cdef class DNSRecord(DNSEntry):

cdef public ttl
cdef public created
cdef public object ttl
cdef public object created

cdef _suppressed_by_answer(self, DNSRecord answer)


cdef class DNSAddress(DNSRecord):

cdef public _hash
cdef public address
cdef public scope_id
cdef public cython.int _hash
cdef public object address
cdef public object scope_id

cdef _eq(self, DNSAddress other)


cdef class DNSHinfo(DNSRecord):

cdef public _hash
cdef public cpu
cdef public os
cdef public cython.int _hash
cdef public object cpu
cdef public object os

cdef _eq(self, DNSHinfo other)


cdef class DNSPointer(DNSRecord):

cdef public _hash
cdef public alias
cdef public cython.int _hash
cdef public object alias

cdef _eq(self, DNSPointer other)


cdef class DNSText(DNSRecord):

cdef public _hash
cdef public text
cdef public cython.int _hash
cdef public object text

cdef _eq(self, DNSText other)


cdef class DNSService(DNSRecord):

cdef public _hash
cdef public priority
cdef public weight
cdef public port
cdef public server
cdef public server_key
cdef public cython.int _hash
cdef public object priority
cdef public object weight
cdef public object port
cdef public object server
cdef public object server_key

cdef _eq(self, DNSService other)


cdef class DNSNsec(DNSRecord):

cdef public _hash
cdef public next_name
cdef public rdtypes
cdef public cython.int _hash
cdef public object next_name
cdef public cython.list rdtypes

cdef _eq(self, DNSNsec other)


cdef class DNSRRSet:

cdef _records
cdef _lookup
cdef cython.dict _lookup

cdef _dns_entry_matches(DNSEntry entry, object key, object type_, object class_)
@cython.locals(other=DNSRecord)
cpdef suppresses(self, DNSRecord record)
94 changes: 51 additions & 43 deletions src/zeroconf/_dns.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,9 +72,12 @@ def __init__(self, name: str, type_: int, class_: int) -> None:
self.class_ = class_ & _CLASS_MASK
self.unique = (class_ & _CLASS_UNIQUE) != 0

def _dns_entry_matches(self, other) -> bool: # type: ignore[no-untyped-def]
return self.key == other.key and self.type == other.type and self.class_ == other.class_

def __eq__(self, other: Any) -> bool:
"""Equality test on key (lowercase name), type, and class"""
return _dns_entry_matches(other, self.key, self.type, self.class_) and isinstance(other, DNSEntry)
return isinstance(other, DNSEntry) and self._dns_entry_matches(other)

@staticmethod
def get_class_(class_: int) -> str:
Expand Down Expand Up @@ -117,7 +120,7 @@ def __hash__(self) -> int:

def __eq__(self, other: Any) -> bool:
"""Tests equality on dns question."""
return isinstance(other, DNSQuestion) and _dns_entry_matches(other, self.key, self.type, self.class_)
return isinstance(other, DNSQuestion) and self._dns_entry_matches(other)

@property
def max_size(self) -> int:
Expand Down Expand Up @@ -169,9 +172,9 @@ def __eq__(self, other: Any) -> bool: # pylint: disable=no-self-use
def suppressed_by(self, msg: 'DNSIncoming') -> bool:
"""Returns true if any answer in a message can suffice for the
information held in this record."""
return any(self.suppressed_by_answer(record) for record in msg.answers)
return any(self._suppressed_by_answer(record) for record in msg.answers)

def suppressed_by_answer(self, other: 'DNSRecord') -> bool:
def _suppressed_by_answer(self, other) -> bool: # type: ignore[no-untyped-def]
"""Returns true if another record has same name, type and class,
and if its TTL is at least half of this record's."""
return self == other and other.ttl > (self.ttl / 2)
Expand Down Expand Up @@ -246,11 +249,13 @@ def write(self, out: 'DNSOutgoing') -> None:

def __eq__(self, other: Any) -> bool:
"""Tests equality on address"""
return isinstance(other, DNSAddress) and self._eq(other)

def _eq(self, other) -> bool: # type: ignore[no-untyped-def]
return (
isinstance(other, DNSAddress)
and self.address == other.address
self.address == other.address
and self.scope_id == other.scope_id
and _dns_entry_matches(other, self.key, self.type, self.class_)
and self._dns_entry_matches(other)
)

def __hash__(self) -> int:
Expand Down Expand Up @@ -289,13 +294,12 @@ def write(self, out: 'DNSOutgoing') -> None:
out.write_character_string(self.os.encode('utf-8'))

def __eq__(self, other: Any) -> bool:
"""Tests equality on cpu and os"""
return (
isinstance(other, DNSHinfo)
and self.cpu == other.cpu
and self.os == other.os
and _dns_entry_matches(other, self.key, self.type, self.class_)
)
"""Tests equality on cpu and os."""
return isinstance(other, DNSHinfo) and self._eq(other)

def _eq(self, other) -> bool: # type: ignore[no-untyped-def]
"""Tests equality on cpu and os."""
return self.cpu == other.cpu and self.os == other.os and self._dns_entry_matches(other)

def __hash__(self) -> int:
"""Hash to compare like DNSHinfo."""
Expand Down Expand Up @@ -334,12 +338,12 @@ def write(self, out: 'DNSOutgoing') -> None:
out.write_name(self.alias)

def __eq__(self, other: Any) -> bool:
"""Tests equality on alias"""
return (
isinstance(other, DNSPointer)
and self.alias == other.alias
and _dns_entry_matches(other, self.key, self.type, self.class_)
)
"""Tests equality on alias."""
return isinstance(other, DNSPointer) and self._eq(other)

def _eq(self, other) -> bool: # type: ignore[no-untyped-def]
"""Tests equality on alias."""
return self.alias == other.alias and self._dns_entry_matches(other)

def __hash__(self) -> int:
"""Hash to compare like DNSPointer."""
Expand Down Expand Up @@ -373,12 +377,12 @@ def __hash__(self) -> int:
return self._hash

def __eq__(self, other: Any) -> bool:
"""Tests equality on text"""
return (
isinstance(other, DNSText)
and self.text == other.text
and _dns_entry_matches(other, self.key, self.type, self.class_)
)
"""Tests equality on text."""
return isinstance(other, DNSText) and self._eq(other)

def _eq(self, other) -> bool: # type: ignore[no-untyped-def]
"""Tests equality on text."""
return self.text == other.text and self._dns_entry_matches(other)

def __repr__(self) -> str:
"""String representation"""
Expand Down Expand Up @@ -422,13 +426,16 @@ def write(self, out: 'DNSOutgoing') -> None:

def __eq__(self, other: Any) -> bool:
"""Tests equality on priority, weight, port and server"""
return isinstance(other, DNSService) and self._eq(other)

def _eq(self, other) -> bool: # type: ignore[no-untyped-def]
"""Tests equality on priority, weight, port and server."""
return (
isinstance(other, DNSService)
and self.priority == other.priority
self.priority == other.priority
and self.weight == other.weight
and self.port == other.port
and self.server == other.server
and _dns_entry_matches(other, self.key, self.type, self.class_)
and self._dns_entry_matches(other)
)

def __hash__(self) -> int:
Expand Down Expand Up @@ -478,12 +485,15 @@ def write(self, out: 'DNSOutgoing') -> None:
out.write_string(out_bytes)

def __eq__(self, other: Any) -> bool:
"""Tests equality on cpu and os"""
"""Tests equality on next_name and rdtypes."""
return isinstance(other, DNSNsec) and self._eq(other)

def _eq(self, other) -> bool: # type: ignore[no-untyped-def]
"""Tests equality on next_name and rdtypes."""
return (
isinstance(other, DNSNsec)
and self.next_name == other.next_name
self.next_name == other.next_name
and self.rdtypes == other.rdtypes
and _dns_entry_matches(other, self.key, self.type, self.class_)
and self._dns_entry_matches(other)
)

def __hash__(self) -> int:
Expand All @@ -497,6 +507,9 @@ def __repr__(self) -> str:
)


_DNSRecord = DNSRecord


class DNSRRSet:
"""A set of dns records independent of the ttl."""

Expand All @@ -514,20 +527,15 @@ def lookup(self) -> Dict[DNSRecord, DNSRecord]:
self._lookup = {record: record for record in self._records}
return self._lookup

def suppresses(self, record: DNSRecord) -> bool:
def suppresses(self, record: _DNSRecord) -> bool:
"""Returns true if any answer in the rrset can suffice for the
information held in this record."""
other = self.lookup.get(record)
if self._lookup is None:
other = self.lookup.get(record)
else:
other = self._lookup.get(record)
return bool(other and other.ttl > (record.ttl / 2))

def __contains__(self, record: DNSRecord) -> bool:
"""Returns true if the rrset contains the record."""
return record in self.lookup


_DNSEntry = DNSEntry
_str = str


def _dns_entry_matches(entry: _DNSEntry, key: _str, type_: int, class_: int) -> bool:
return key == entry.key and type_ == entry.type and class_ == entry.class_

0 comments on commit 3a25ff7

Please sign in to comment.