Skip to content

SearchManager: partition uint32 search-ID space by top bit - #533

Closed
got3nks wants to merge 1 commit into
amule-project:masterfrom
got3nks:pr-search-id-partition
Closed

SearchManager: partition uint32 search-ID space by top bit#533
got3nks wants to merge 1 commit into
amule-project:masterfrom
got3nks:pr-search-id-partition

Conversation

@got3nks

@got3nks got3nks commented May 7, 2026

Copy link
Copy Markdown
Contributor

Alternative to #530 — closing the same bug at the structural level rather than buffering past it. Bug + dual-counter root-cause diagnosis are both @danim7's — see #530 for the full repro with screenshots and the analysis pointing at the two-counter collision.

Bug

CSearchManager::m_nextID (Kad-allocated search IDs) and CSearchDlg::m_nSearchID (ed2k Local/Global search IDs) are two independent counters that both start at 0 and increment freely on every search. CSearchList keys results by search ID across both networks, so a long-enough session lets the two counters collide and route Kad results into the ed2k tab (or vice versa).

Why this approach

#530 offsets the Kad counter by 1024 — a buffer that delays the collision but does not prevent it: a session with ≥1024 user-initiated Local/Global searches re-introduces the bug.

This patch partitions the 32-bit ID space by the top bit so the two allocators can never produce equal values, regardless of session length:

Range Top bit Allocator
0x000000000x7FFFFFFF 0 ed2k (CSearchDlg::m_nSearchID)
0x800000000xFFFFFEFF 1 Kad (CSearchManager::m_nextID)

The two sets are disjoint by construction. The partition also fits the existing convention in this same file — SearchManager.cpp:169 already reserves the top byte (0xFFFFFF00..0xFFFFFFFF) for the related-search "use supplied ID as-is" path, so high-bit reservations of the search-ID space are already a pattern; this just splits the remaining space cleanly between the two ID-incrementing allocators.

Diff

Two surgical edits, both call-site preserving (no cross-module refactor → amule-remote-gui link unaffected, which was the snag in #530's single-shared-counter attempt):

- uint32_t  CSearchManager::m_nextID = 0;
+ // ... comment block explaining the partition contract ...
+ uint32_t  CSearchManager::m_nextID = 0x80000000;
- m_nSearchID++;
+ // ... comment explaining the mask ...
+ m_nSearchID = (m_nSearchID + 1) & 0x7fffffff;

The mask in SearchDlg.cpp keeps ed2k IDs in the bottom half even after 2^31 increments (where an unmasked ++ would wrap into the Kad range).

CSearchManager::m_nextID (Kad-allocated search IDs) and
CSearchDlg::m_nSearchID (ed2k Local/Global search IDs) both start at
0 and increment freely on every search. CSearchList keys results by
search ID across both networks, so a long enough session lets the two
counters collide and route Kad results into the ed2k tab (or vice
versa). Reported as amule-project#530, where the proposed fix offsets the Kad
counter by 1024 — an empirical buffer that delays the collision but
does not prevent it (a session with >=1024 user-initiated Local /
Global searches re-introduces the bug).

Partition the 32-bit ID space by the top bit so the two allocators
can never produce equal values regardless of session length:

  ed2k IDs   0x00000000 .. 0x7FFFFFFF   (top bit = 0)
  Kad IDs    0x80000000 .. 0xFFFFFEFF   (top bit = 1)

The two sets are disjoint by construction. This fits the existing
convention of high-bit tagging in this same file: SearchManager.cpp
already reserves the top byte (0xFFFFFF00 .. 0xFFFFFFFF) for the
related-search "use supplied ID as-is" path, so high-bit reservations
are already a pattern; this just splits the remaining space cleanly
between the two allocators.

Two surgical edits, both call-site preserving:

- src/kademlia/kademlia/SearchManager.cpp:59
  m_nextID starts at 0x80000000 instead of 0. The Kad counter only
  ever increments, so it stays in the top half. Comment block on the
  decl spells out the partition contract.

- src/SearchDlg.cpp:509
  m_nSearchID++  ->  m_nSearchID = (m_nSearchID + 1) & 0x7fffffff
  The mask clears the top bit on every increment, keeping ed2k IDs
  in the bottom half even after 2^31 increments (where unmasked
  ++ would wrap into the Kad range).

Both counters stay where they are; no cross-module refactor, so the
amule-remote-gui link doesn't break the way amule-project#530's single-shared-
counter attempt did.
@got3nks

got3nks commented May 7, 2026

Copy link
Copy Markdown
Contributor Author

Superseded by #530, which now incorporates the top-bit partition with the overflow-safe mask-on-emit + dead-code cleanup.

@got3nks got3nks closed this May 7, 2026
@got3nks
got3nks deleted the pr-search-id-partition branch May 7, 2026 12:00
got3nks added a commit to got3nks/amule that referenced this pull request Jul 20, 2026
…ect#533)

The Flatpak job regenerates ECCodes.h from ECCodes.abstract rather than using the committed header, and amule-project#530's comments in that file made the generated header unparseable ("invalid preprocessing directive #=" plus a truncated ECOpCodes enum), failing every Flatpak build on master.

The generator only treats a line as a comment when it matches "^#" with no leading whitespace, so the comments indented to line up with the tag entries fell through to the data path and had every word turned into a field. It also flattens the file into a CMake list by replacing newlines with ";", so a semicolon inside a comment splits the line and the remainder loses its leading "#".

Documentation for these codes lives in the hand-maintained header, so the abstract keeps to entries. Generating before and after: master produces 8 mangled lines and 11 compile errors, this produces none, with all seven new codes intact.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant