Skip to content

Commit b0d9a54

Browse files
authored
Merge branch 'main' into koan/cache-human-readable-name
2 parents 3ae23a7 + 967cc9f commit b0d9a54

13 files changed

Lines changed: 153 additions & 13 deletions
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
from bluetooth_data_tools import calculate_distance_meters
2+
3+
_CLOSE_POWER = -4
4+
_CLOSE_RSSI = -3
5+
6+
_FAR_POWER = -59
7+
_FAR_RSSI = -60
8+
9+
_ZERO_POWER = 59
10+
_ZERO_RSSI = 0
11+
12+
13+
def test_calculate_distance_meters_close(benchmark):
14+
benchmark(lambda: calculate_distance_meters(_CLOSE_POWER, _CLOSE_RSSI))
15+
16+
17+
def test_calculate_distance_meters_far(benchmark):
18+
benchmark(lambda: calculate_distance_meters(_FAR_POWER, _FAR_RSSI))
19+
20+
21+
def test_calculate_distance_meters_zero(benchmark):
22+
benchmark(lambda: calculate_distance_meters(_ZERO_POWER, _ZERO_RSSI))

bench/test_human_readable_name.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
from bluetooth_data_tools import human_readable_name
2+
3+
_NAME = "Living Room Sensor"
4+
_LOCAL_NAME = "RZSS"
5+
_ADDRESS = "AA:BB:CC:DD:EE:FF"
6+
7+
8+
def test_human_readable_name_with_name(benchmark):
9+
benchmark(lambda: human_readable_name(_NAME, _LOCAL_NAME, _ADDRESS))
10+
11+
12+
def test_human_readable_name_local_only(benchmark):
13+
benchmark(lambda: human_readable_name(None, _LOCAL_NAME, _ADDRESS))

bench/test_parse_gap.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
from bluetooth_data_tools import parse_advertisement_data
2+
from bluetooth_data_tools.gap import _parse_advertisement_data
23

34
# cythonize -X language_level=3 -a -i src/bluetooth_data_tools/gap.py
45

@@ -83,3 +84,14 @@
8384

8485
def test_parse_advertisement_data(benchmark):
8586
benchmark(lambda: parse_advertisement_data(advs))
87+
88+
89+
def test_parse_advertisement_data_bytes_cache_fallthrough(benchmark):
90+
advs_as_single_tuple = (b"".join(advs),)
91+
parse_advertisement_data(advs_as_single_tuple)
92+
93+
def run():
94+
_parse_advertisement_data.cache_clear()
95+
parse_advertisement_data(advs_as_single_tuple)
96+
97+
benchmark(run)
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
from bluetooth_data_tools.gap import _uint128_bytes_as_uuid
2+
3+
_UUID_BYTES = bytes(range(16))
4+
5+
6+
def test_uint128_bytes_as_uuid_cached(benchmark):
7+
benchmark(lambda: _uint128_bytes_as_uuid(_UUID_BYTES))
8+
9+
10+
def test_uint128_bytes_as_uuid_uncached(benchmark):
11+
def run():
12+
_uint128_bytes_as_uuid.cache_clear()
13+
_uint128_bytes_as_uuid(_UUID_BYTES)
14+
15+
benchmark(run)

docs/conf.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
project = "bluetooth-data-tools"
88
copyright = "2023, J. Nick Koston"
99
author = "J. Nick Koston"
10-
release = "1.29.15"
10+
release = "1.29.16"
1111

1212
# General configuration
1313
extensions = [

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ build-backend = "poetry.core.masonry.api"
44

55
[project]
66
name = "bluetooth-data-tools"
7-
version = "1.29.15"
7+
version = "1.29.16"
88
license = "Apache-2.0"
99
description = "Tools for converting bluetooth data and packets"
1010
authors = [{ name = "J. Nick Koston", email = "nick@koston.org" }]

src/bluetooth_data_tools/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
short_address,
2222
)
2323

24-
__version__ = "1.29.15"
24+
__version__ = "1.29.16"
2525

2626

2727
__all__ = [

src/bluetooth_data_tools/gap.pxd

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,6 @@ cdef dict _EMPTY_MANUFACTURER_DATA
77
cdef dict _EMPTY_SERVICE_DATA
88
cdef list _EMPTY_SERVICE_UUIDS
99

10-
cdef object from_bytes
11-
cdef object from_bytes_little
12-
1310
cdef object _cached_uint16_int_as_uuid
1411
cdef object _cached_uint32_int_as_uuid
1512
cdef object _cached_uint128_bytes_as_uuid

src/bluetooth_data_tools/gap.py

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import logging
44
from collections.abc import Iterable
55
from enum import IntEnum
6-
from functools import lru_cache, partial
6+
from functools import lru_cache
77
from typing import TYPE_CHECKING
88

99
BLE_UUID = "0000-1000-8000-00805f9b34fb"
@@ -75,9 +75,6 @@ class BLEGAPType(IntEnum):
7575
TYPE_MANUFACTURER_SPECIFIC_DATA = 0xFF
7676

7777

78-
from_bytes = int.from_bytes
79-
from_bytes_little = partial(from_bytes, byteorder="little")
80-
8178
# Signed-fold constants for the one-byte TX Power Level decode.
8279
# A uint8 value at or above _INT8_SIGN_THRESHOLD is negative when interpreted
8380
# as int8, and recovers its signed value via subtraction of _INT8_RANGE.
@@ -113,9 +110,11 @@ class BLEGAPType(IntEnum):
113110

114111
@lru_cache(maxsize=256)
115112
def _uint128_bytes_as_uuid(uint128_bytes: bytes_) -> str:
116-
"""Convert an integer to a UUID str."""
117-
int_value = from_bytes_little(uint128_bytes)
118-
hex = f"{int_value:032x}"
113+
"""Convert 16 little-endian bytes to a canonical UUID str."""
114+
# bytes.hex() is a single C call; reversing the 16-byte slice yields the
115+
# big-endian hex form directly, skipping the int.from_bytes + format-spec
116+
# round-trip used previously (~40% faster on the cache-miss path).
117+
hex = uint128_bytes[::-1].hex()
119118
return f"{hex[:8]}-{hex[8:12]}-{hex[12:16]}-{hex[16:20]}-{hex[20:]}"
120119

121120

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
from pytest_codspeed import BenchmarkFixture
2+
3+
from bluetooth_data_tools import calculate_distance_meters
4+
5+
# Close: ratio < 1, takes the pow(ratio, 10) branch.
6+
_CLOSE_POWER = -4
7+
_CLOSE_RSSI = -3
8+
9+
# Far: ratio > 1, takes the 0.89976 * pow(ratio, 7.7095) + 0.111 branch.
10+
_FAR_POWER = -59
11+
_FAR_RSSI = -60
12+
13+
# Zero rssi short circuits to None before any pow call.
14+
_ZERO_POWER = 59
15+
_ZERO_RSSI = 0
16+
17+
18+
def test_calculate_distance_meters_close(benchmark: BenchmarkFixture) -> None:
19+
benchmark(lambda: calculate_distance_meters(_CLOSE_POWER, _CLOSE_RSSI))
20+
21+
22+
def test_calculate_distance_meters_far(benchmark: BenchmarkFixture) -> None:
23+
benchmark(lambda: calculate_distance_meters(_FAR_POWER, _FAR_RSSI))
24+
25+
26+
def test_calculate_distance_meters_zero(benchmark: BenchmarkFixture) -> None:
27+
benchmark(lambda: calculate_distance_meters(_ZERO_POWER, _ZERO_RSSI))

0 commit comments

Comments
 (0)