tests: add tests for 32bit high bit set in uuid parser#253
Conversation
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #253 +/- ##
=========================================
Coverage 100.00% 100.00%
=========================================
Files 6 6
Lines 266 266
Branches 42 42
=========================================
Hits 266 266 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
PR Review — perf(gap): hoist per-iter UUID length bounds check out of parse loopCode change is technically correct (safe_end stride-truncation drops the malformed tail equivalently to the old 🔴 Blocking1. Maintainer requested: drop code change, keep tests (`src/bluetooth_data_tools/gap.py`, L253-323)@bdraco's most recent review is explicit: "The tests are still valuable, but the code change is not worth the churn. remove the code change from this pr and keep the tests". The codspeed run on this branch reports 🟡 Important1. PR description / Quality Report test-status mismatchThe PR description's Testing section says 🟢 Suggestions1. Unsigned staging is correct but only the 32-bit branch needs it (`src/bluetooth_data_tools/gap.py`, L272-286)If the code change is kept, note the asymmetry: the 16-bit branch still uses inline 2. Good regression coverage for bit-31 UUIDs (`tests/test_gap.py`, L1116-1148)These two tests are the right shape — unique byte patterns (per the project learning that shared AD payloads alias through the Checklist
SummaryCode change is technically correct (safe_end stride-truncation drops the malformed tail equivalently to the old To rebase specific severity levels, mention me: Automated review by Kōan3985a70 |
Rebase with requested adjustmentsBranch StatsActions performed
CI statusCI will be checked asynchronously. Automated by Kōan |
9b4d5cb to
2829b82
Compare
There was a problem hiding this comment.
Pull request overview
This PR optimizes BLE GAP advertisement parsing in bluetooth_data_tools.gap by removing per-iteration bounds checks in the 16/32/128-bit Service UUID list parsing loops, while preserving the existing behavior of dropping malformed trailing bytes.
Changes:
- Compute a
safe_end(multiple-of-stride endpoint) for 16/32/128-bit UUID list payloads and iterate withrange(start, safe_end, N)to make the loop bodies branch-free. - Add
safe_endto the Cython locals declaration ingap.pxdto keep Cython compilation consistent.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
src/bluetooth_data_tools/gap.py |
Hoists UUID list bounds checks out of the hot parse loops by truncating to a stride-aligned endpoint. |
src/bluetooth_data_tools/gap.pxd |
Declares the new safe_end local for Cython-typed compilation. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| # Assemble via uint local: in Cython the shift-by-24 of an | ||
| # unsigned char promotes to signed int and would yield a | ||
| # negative value when bit 31 is set; assigning to a | ||
| # cython.uint local recovers the unsigned 32-bit value. | ||
| uuid32_int = ( | ||
| gap_data[i] | ||
| | (gap_data[i + 1] << 8) | ||
| | (gap_data[i + 2] << 16) | ||
| | (gap_data[i + 3] << 24) | ||
| ) |
Rebase with requested adjustmentsBranch Changes applied
StatsActions performed
CI statusCI will be checked asynchronously. Automated by Kōan |
2829b82 to
4517c45
Compare
We need to add tests to make sure we pin handling of this case and we don't have a unsigned overflow |
Agreed — the cython.uint staging is invisible to the existing 32-bit UUID payloads in
and assert the resulting |
Rebase with requested adjustmentsBranch StatsActions performed
CI statusCI will be checked asynchronously. Automated by Kōan |
d6af62a to
c94c88d
Compare
| # Truncate to a multiple-of-2 endpoint so the loop body has no | ||
| # per-iteration bounds branch; any odd tail is dropped. | ||
| safe_end = start + ((end - start) & ~1) | ||
| for i in range(start, safe_end, 2): |
Concrete test shape that would pin both halves of the concern: def test_parse_advertisement_data_32bit_uuid_high_bit_set():
# TYPE_32BIT_SERVICE_UUID_COMPLETE (0x05) carrying three UUIDs with bit 31 set.
# Length byte = 1 (type) + 3*4 (UUIDs) = 13.
payload = bytes([
13, 0x05,
0xFF, 0xFF, 0xFF, 0xFF, # 0xFFFFFFFF
0x00, 0x00, 0x00, 0x80, # 0x80000000
0x01, 0x00, 0x00, 0x80, # 0x80000001
])
result = _uncached_parse_advertisement_bytes(payload)
assert UUID(int=0xFFFFFFFF * (1 << 96) | BASE_UUID_INT) in result.service_uuids
# ...etc, asserting all three round-trip via _cached_uint32_int_as_uuidAnd a mirror for The existing #231 tail-drop tests already cover the malformed-length safe_end behavior, so the only new coverage needed is the unsigned-shift pin. Happy to add these in a follow-up commit on this branch if useful. |
Rebase with requested adjustmentsBranch Changes applied
StatsActions performed
CI statusCI will be checked asynchronously. Automated by Kōan |
c6b4bc7 to
8dbeb52
Compare
bdraco
left a comment
There was a problem hiding this comment.
codspeed shows no improvement, either open another PR to add benchmarks ahead of this pr to prove it improves performance, or close this PR as its just churn otherwise.
The latest revision adds two tests that pin unsigned 32-bit decoding for both branches you flagged:
Without the cython.uint staging on the gap_data[i+3] operand, Separately, on the broader churn concern: the codspeed delta still reads zero, which I think reflects the benchmark suite not exercising the multi-UUID-list loop bodies (the dominant cost in test_parse_gap_bytes is the 128-bit slice). The right next step is probably to land the multi-UUID-list benchmarks from PR #258 first (if not already on main) so the hoist has a benchmark to move. |
For 16/32/128-bit Service UUID AD lists, the parse loop ran a `i + N <= end` check on every iteration to discard any malformed short tail (per Bluetooth-Devices#226). Compute the multiple-of-stride endpoint once before the loop instead, so the loop body has no per-iteration bounds branch. Malformed-tail behavior is preserved (the bits `(end - start) & ~(N-1)` truncate any 1..N-1 byte remainder). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Rebase with requested adjustmentsBranch StatsActions performed
CI statusCI will be checked asynchronously. Automated by Kōan |
8dbeb52 to
076c09d
Compare
bdraco
left a comment
There was a problem hiding this comment.
The tests are still valuable, but the code change is not worth the churn. remove the code change from this pr and keep the tests
Tests for this case are now present in
Both use unique byte patterns to avoid cross-test aliasing through the |
Rebase with requested adjustmentsBranch Changes applied
StatsActions performed
CI statusCI will be checked asynchronously. Automated by Kōan |
Add tests for 32bit high bit set in uuid parser