pycyphal2 2.0.0.dev4__tar.gz → 2.0.0.dev5__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. {pycyphal2-2.0.0.dev4/src/pycyphal2.egg-info → pycyphal2-2.0.0.dev5}/PKG-INFO +1 -1
  2. {pycyphal2-2.0.0.dev4 → pycyphal2-2.0.0.dev5}/src/pycyphal2/__init__.py +1 -1
  3. {pycyphal2-2.0.0.dev4 → pycyphal2-2.0.0.dev5}/src/pycyphal2/_node.py +3 -1
  4. {pycyphal2-2.0.0.dev4 → pycyphal2-2.0.0.dev5/src/pycyphal2.egg-info}/PKG-INFO +1 -1
  5. {pycyphal2-2.0.0.dev4 → pycyphal2-2.0.0.dev5}/tests/test_integration.py +2 -2
  6. {pycyphal2-2.0.0.dev4 → pycyphal2-2.0.0.dev5}/tests/test_topic.py +37 -10
  7. {pycyphal2-2.0.0.dev4 → pycyphal2-2.0.0.dev5}/LICENSE +0 -0
  8. {pycyphal2-2.0.0.dev4 → pycyphal2-2.0.0.dev5}/README.md +0 -0
  9. {pycyphal2-2.0.0.dev4 → pycyphal2-2.0.0.dev5}/pyproject.toml +0 -0
  10. {pycyphal2-2.0.0.dev4 → pycyphal2-2.0.0.dev5}/setup.cfg +0 -0
  11. {pycyphal2-2.0.0.dev4 → pycyphal2-2.0.0.dev5}/src/pycyphal2/_api.py +0 -0
  12. {pycyphal2-2.0.0.dev4 → pycyphal2-2.0.0.dev5}/src/pycyphal2/_hash.py +0 -0
  13. {pycyphal2-2.0.0.dev4 → pycyphal2-2.0.0.dev5}/src/pycyphal2/_header.py +0 -0
  14. {pycyphal2-2.0.0.dev4 → pycyphal2-2.0.0.dev5}/src/pycyphal2/_publisher.py +0 -0
  15. {pycyphal2-2.0.0.dev4 → pycyphal2-2.0.0.dev5}/src/pycyphal2/_subscriber.py +0 -0
  16. {pycyphal2-2.0.0.dev4 → pycyphal2-2.0.0.dev5}/src/pycyphal2/_transport.py +0 -0
  17. {pycyphal2-2.0.0.dev4 → pycyphal2-2.0.0.dev5}/src/pycyphal2/can/__init__.py +0 -0
  18. {pycyphal2-2.0.0.dev4 → pycyphal2-2.0.0.dev5}/src/pycyphal2/can/_interface.py +0 -0
  19. {pycyphal2-2.0.0.dev4 → pycyphal2-2.0.0.dev5}/src/pycyphal2/can/_media_slcan.py +0 -0
  20. {pycyphal2-2.0.0.dev4 → pycyphal2-2.0.0.dev5}/src/pycyphal2/can/_reassembly.py +0 -0
  21. {pycyphal2-2.0.0.dev4 → pycyphal2-2.0.0.dev5}/src/pycyphal2/can/_transport.py +0 -0
  22. {pycyphal2-2.0.0.dev4 → pycyphal2-2.0.0.dev5}/src/pycyphal2/can/_wire.py +0 -0
  23. {pycyphal2-2.0.0.dev4 → pycyphal2-2.0.0.dev5}/src/pycyphal2/can/pythoncan.py +0 -0
  24. {pycyphal2-2.0.0.dev4 → pycyphal2-2.0.0.dev5}/src/pycyphal2/can/socketcan.py +0 -0
  25. {pycyphal2-2.0.0.dev4 → pycyphal2-2.0.0.dev5}/src/pycyphal2/can/webserial.py +0 -0
  26. {pycyphal2-2.0.0.dev4 → pycyphal2-2.0.0.dev5}/src/pycyphal2/py.typed +0 -0
  27. {pycyphal2-2.0.0.dev4 → pycyphal2-2.0.0.dev5}/src/pycyphal2/udp.py +0 -0
  28. {pycyphal2-2.0.0.dev4 → pycyphal2-2.0.0.dev5}/src/pycyphal2.egg-info/SOURCES.txt +0 -0
  29. {pycyphal2-2.0.0.dev4 → pycyphal2-2.0.0.dev5}/src/pycyphal2.egg-info/dependency_links.txt +0 -0
  30. {pycyphal2-2.0.0.dev4 → pycyphal2-2.0.0.dev5}/src/pycyphal2.egg-info/requires.txt +0 -0
  31. {pycyphal2-2.0.0.dev4 → pycyphal2-2.0.0.dev5}/src/pycyphal2.egg-info/top_level.txt +0 -0
  32. {pycyphal2-2.0.0.dev4 → pycyphal2-2.0.0.dev5}/tests/test_gossip.py +0 -0
  33. {pycyphal2-2.0.0.dev4 → pycyphal2-2.0.0.dev5}/tests/test_hash.py +0 -0
  34. {pycyphal2-2.0.0.dev4 → pycyphal2-2.0.0.dev5}/tests/test_header.py +0 -0
  35. {pycyphal2-2.0.0.dev4 → pycyphal2-2.0.0.dev5}/tests/test_monitor.py +0 -0
  36. {pycyphal2-2.0.0.dev4 → pycyphal2-2.0.0.dev5}/tests/test_names.py +0 -0
  37. {pycyphal2-2.0.0.dev4 → pycyphal2-2.0.0.dev5}/tests/test_parity.py +0 -0
  38. {pycyphal2-2.0.0.dev4 → pycyphal2-2.0.0.dev5}/tests/test_parity_coverage.py +0 -0
  39. {pycyphal2-2.0.0.dev4 → pycyphal2-2.0.0.dev5}/tests/test_pubsub.py +0 -0
  40. {pycyphal2-2.0.0.dev4 → pycyphal2-2.0.0.dev5}/tests/test_reliable.py +0 -0
  41. {pycyphal2-2.0.0.dev4 → pycyphal2-2.0.0.dev5}/tests/test_reorder.py +0 -0
  42. {pycyphal2-2.0.0.dev4 → pycyphal2-2.0.0.dev5}/tests/test_rpc.py +0 -0
  43. {pycyphal2-2.0.0.dev4 → pycyphal2-2.0.0.dev5}/tests/test_scout.py +0 -0
  44. {pycyphal2-2.0.0.dev4 → pycyphal2-2.0.0.dev5}/tests/test_udp.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pycyphal2
3
- Version: 2.0.0.dev4
3
+ Version: 2.0.0.dev5
4
4
  Summary: Pure-Python implementation of Cyphal -- a simple and robust real-time publish/subscribe stack that runs anywhere.
5
5
  Author-email: Pavel Kirienko and OpenCyphal team <pavel@opencyphal.org>
6
6
  License: MIT
@@ -155,7 +155,7 @@ from ._transport import SubjectWriter as SubjectWriter
155
155
  from ._transport import Transport as Transport
156
156
  from ._transport import TransportArrival as TransportArrival
157
157
 
158
- __version__ = "2.0.0.dev4"
158
+ __version__ = "2.0.0.dev5"
159
159
 
160
160
  # pdoc needs __all__ to display re-exported members.
161
161
  __all__ = [
@@ -246,7 +246,9 @@ def compute_subject_id(topic_hash: int, evictions: int, modulus: int) -> int:
246
246
  """Compute the subject-ID for a topic given its hash, evictions, and subject-ID modulus."""
247
247
  if evictions >= EVICTIONS_PINNED_MIN:
248
248
  return 0xFFFFFFFF - evictions
249
- return SUBJECT_ID_PINNED_MAX + 1 + ((topic_hash + (evictions * evictions)) % modulus)
249
+ h = topic_hash % modulus
250
+ e = evictions % modulus
251
+ return SUBJECT_ID_PINNED_MAX + 1 + ((h + ((e * e) % modulus)) % modulus)
250
252
 
251
253
 
252
254
  # =====================================================================================================================
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pycyphal2
3
- Version: 2.0.0.dev4
3
+ Version: 2.0.0.dev5
4
4
  Summary: Pure-Python implementation of Cyphal -- a simple and robust real-time publish/subscribe stack that runs anywhere.
5
5
  Author-email: Pavel Kirienko and OpenCyphal team <pavel@opencyphal.org>
6
6
  License: MIT
@@ -270,12 +270,12 @@ async def test_subject_id_computation():
270
270
  """Verify subject-ID computation matches the reference formula."""
271
271
  modulus = 8378431 # 23bit
272
272
 
273
- # Non-pinned: 0x2000 + ((hash + evictions^2) % modulus)
273
+ # Non-pinned: 0x2000 + (((hash % modulus) + ((evictions % modulus)^2 % modulus)) % modulus)
274
274
  sid = compute_subject_id(0xDEADBEEF, 0, modulus)
275
275
  assert sid == 0x2000 + (0xDEADBEEF % modulus)
276
276
 
277
277
  sid = compute_subject_id(0xDEADBEEF, 3, modulus)
278
- assert sid == 0x2000 + ((0xDEADBEEF + 9) % modulus)
278
+ assert sid == 0x2000 + (((0xDEADBEEF % modulus) + ((3 % modulus) ** 2 % modulus)) % modulus)
279
279
 
280
280
  # Pinned: UINT32_MAX - evictions
281
281
  sid = compute_subject_id(0xDEADBEEF, EVICTIONS_PINNED_MIN, modulus)
@@ -40,21 +40,40 @@ def test_compute_subject_id_pinned_boundary():
40
40
 
41
41
  def test_compute_subject_id_non_pinned_zero_evictions():
42
42
  """Non-pinned with zero evictions: offset + hash % modulus."""
43
- topic_hash = rapidhash("my/topic")
44
- sid = compute_subject_id(topic_hash, 0, DEFAULT_MODULUS)
45
- expected = SUBJECT_ID_PINNED_MAX + 1 + (topic_hash % DEFAULT_MODULUS)
46
- assert sid == expected
43
+ for topic_hash in (0, 1, rapidhash("my/topic"), (1 << 64) - 1):
44
+ sid = compute_subject_id(topic_hash, 0, DEFAULT_MODULUS)
45
+ expected = SUBJECT_ID_PINNED_MAX + 1 + (topic_hash % DEFAULT_MODULUS)
46
+ assert sid == expected
47
47
 
48
48
 
49
49
  def test_compute_subject_id_non_pinned_with_evictions():
50
- """Non-pinned formula: offset + (hash + evictions^2) % modulus."""
50
+ """Non-pinned formula: offset + ((hash % modulus) + ((evictions % modulus)^2 % modulus)) % modulus."""
51
51
  topic_hash = rapidhash("some/topic")
52
52
  for ev in (1, 2, 5, 100):
53
53
  sid = compute_subject_id(topic_hash, ev, DEFAULT_MODULUS)
54
- expected = SUBJECT_ID_PINNED_MAX + 1 + ((topic_hash + ev * ev) % DEFAULT_MODULUS)
54
+ expected = (
55
+ SUBJECT_ID_PINNED_MAX
56
+ + 1
57
+ + (
58
+ ((topic_hash % DEFAULT_MODULUS) + (((ev % DEFAULT_MODULUS) * (ev % DEFAULT_MODULUS)) % DEFAULT_MODULUS))
59
+ % DEFAULT_MODULUS
60
+ )
61
+ )
55
62
  assert sid == expected
56
63
 
57
64
 
65
+ def test_compute_subject_id_non_pinned_does_not_wrap_uint64_sum():
66
+ topic_hash = (1 << 64) - 1
67
+ evictions = EVICTIONS_PINNED_MIN - 1
68
+ sid = compute_subject_id(topic_hash, evictions, DEFAULT_MODULUS)
69
+ uint64_wrapping = (
70
+ SUBJECT_ID_PINNED_MAX + 1 + (((topic_hash + (evictions * evictions)) & ((1 << 64) - 1)) % DEFAULT_MODULUS)
71
+ )
72
+ assert sid == 49564
73
+ assert uint64_wrapping == 74897
74
+ assert sid != uint64_wrapping
75
+
76
+
58
77
  def test_compute_subject_id_evictions_changes_sid():
59
78
  """Different eviction counts should generally produce different subject-IDs."""
60
79
  topic_hash = rapidhash("test/evictions")
@@ -70,7 +89,14 @@ def test_compute_subject_id_just_below_pinned():
70
89
  ev = EVICTIONS_PINNED_MIN - 1
71
90
  topic_hash = 12345
72
91
  sid = compute_subject_id(topic_hash, ev, DEFAULT_MODULUS)
73
- expected = SUBJECT_ID_PINNED_MAX + 1 + ((topic_hash + ev * ev) % DEFAULT_MODULUS)
92
+ expected = (
93
+ SUBJECT_ID_PINNED_MAX
94
+ + 1
95
+ + (
96
+ ((topic_hash % DEFAULT_MODULUS) + (((ev % DEFAULT_MODULUS) * (ev % DEFAULT_MODULUS)) % DEFAULT_MODULUS))
97
+ % DEFAULT_MODULUS
98
+ )
99
+ )
74
100
  assert sid == expected
75
101
 
76
102
 
@@ -361,9 +387,10 @@ async def test_gossip_unknown_collision_we_lose():
361
387
  # produces the same subject-ID and a very high lage.
362
388
  remote_lage = 50 # Very old.
363
389
  # Build a fake hash that produces the same SID as our topic.
364
- # Since sid = PINNED_MAX + 1 + (hash + ev^2) % modulus, we need:
365
- # (remote_hash + 0) % modulus == (topic.hash + old_evictions^2) % modulus
366
- target_remainder = (topic.hash + old_evictions * old_evictions) % DEFAULT_MODULUS
390
+ target_remainder = (
391
+ (topic.hash % DEFAULT_MODULUS)
392
+ + (((old_evictions % DEFAULT_MODULUS) * (old_evictions % DEFAULT_MODULUS)) % DEFAULT_MODULUS)
393
+ ) % DEFAULT_MODULUS
367
394
  # Pick remote_hash such that remote_hash % modulus == target_remainder AND remote_hash != topic.hash.
368
395
  remote_hash = target_remainder + DEFAULT_MODULUS # Different from topic.hash but same modular result.
369
396
 
File without changes
File without changes
File without changes