librats 1.0.2 → 2.2.0

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 (319) hide show
  1. package/README.md +145 -331
  2. package/binding.gyp +16 -3
  3. package/lib/index.d.ts +288 -696
  4. package/lib/index.js +407 -44
  5. package/native-src/3rdparty/android/ifaddrs-android.c +1 -0
  6. package/native-src/3rdparty/android/ifaddrs-android.h +1 -0
  7. package/native-src/CMakeLists.txt +404 -179
  8. package/native-src/LICENSE +1 -1
  9. package/native-src/src/librats/bindings/rats.cpp +762 -0
  10. package/native-src/src/librats/bindings/rats.h +380 -0
  11. package/native-src/src/librats/bittorrent/bencode.cpp +437 -0
  12. package/native-src/src/librats/bittorrent/bencode.h +176 -0
  13. package/native-src/src/librats/bittorrent/bitfield.cpp +97 -0
  14. package/native-src/src/librats/bittorrent/bitfield.h +76 -0
  15. package/native-src/src/librats/bittorrent/byte_io.h +58 -0
  16. package/native-src/src/librats/bittorrent/choker.cpp +25 -0
  17. package/native-src/src/librats/bittorrent/choker.h +46 -0
  18. package/native-src/src/librats/bittorrent/client.cpp +413 -0
  19. package/native-src/src/librats/bittorrent/client.h +227 -0
  20. package/native-src/src/librats/bittorrent/disk_io.cpp +209 -0
  21. package/native-src/src/librats/bittorrent/disk_io.h +150 -0
  22. package/native-src/src/librats/bittorrent/extensions.cpp +191 -0
  23. package/native-src/src/librats/bittorrent/extensions.h +94 -0
  24. package/native-src/src/librats/bittorrent/file_storage.cpp +77 -0
  25. package/native-src/src/librats/bittorrent/file_storage.h +79 -0
  26. package/native-src/src/librats/bittorrent/log.h +42 -0
  27. package/native-src/src/librats/bittorrent/magnet_uri.cpp +98 -0
  28. package/native-src/src/librats/bittorrent/magnet_uri.h +35 -0
  29. package/native-src/src/librats/bittorrent/peer_connection.cpp +502 -0
  30. package/native-src/src/librats/bittorrent/peer_connection.h +194 -0
  31. package/native-src/src/librats/bittorrent/peer_list.cpp +68 -0
  32. package/native-src/src/librats/bittorrent/peer_list.h +75 -0
  33. package/native-src/src/librats/bittorrent/piece_picker.cpp +352 -0
  34. package/native-src/src/librats/bittorrent/piece_picker.h +201 -0
  35. package/native-src/src/librats/bittorrent/reactor.cpp +97 -0
  36. package/native-src/src/librats/bittorrent/reactor.h +89 -0
  37. package/native-src/src/librats/bittorrent/resume_data.cpp +72 -0
  38. package/native-src/src/librats/bittorrent/resume_data.h +41 -0
  39. package/native-src/src/librats/bittorrent/store_buffer.cpp +48 -0
  40. package/native-src/src/librats/bittorrent/store_buffer.h +47 -0
  41. package/native-src/src/librats/bittorrent/torrent.cpp +870 -0
  42. package/native-src/src/librats/bittorrent/torrent.h +260 -0
  43. package/native-src/src/librats/bittorrent/torrent_creator.cpp +129 -0
  44. package/native-src/src/librats/bittorrent/torrent_creator.h +58 -0
  45. package/native-src/src/librats/bittorrent/torrent_info.cpp +314 -0
  46. package/native-src/src/librats/bittorrent/torrent_info.h +118 -0
  47. package/native-src/src/librats/bittorrent/tracker.cpp +374 -0
  48. package/native-src/src/librats/bittorrent/tracker.h +108 -0
  49. package/native-src/src/librats/bittorrent/types.cpp +206 -0
  50. package/native-src/src/librats/bittorrent/types.h +86 -0
  51. package/native-src/src/librats/core/address.cpp +35 -0
  52. package/native-src/src/librats/core/address.h +78 -0
  53. package/native-src/src/librats/core/bytes.h +69 -0
  54. package/native-src/src/librats/core/chained_send_buffer.cpp +172 -0
  55. package/native-src/src/librats/core/chained_send_buffer.h +183 -0
  56. package/native-src/src/librats/core/endpoint_parse.cpp +41 -0
  57. package/native-src/src/librats/core/endpoint_parse.h +31 -0
  58. package/native-src/src/librats/core/event_bus.h +70 -0
  59. package/native-src/src/librats/core/host_endpoint.h +56 -0
  60. package/native-src/src/{io_poller.cpp → librats/core/io_poller.cpp} +520 -65
  61. package/native-src/src/{io_poller.h → librats/core/io_poller.h} +12 -6
  62. package/native-src/src/librats/core/ip_address.cpp +120 -0
  63. package/native-src/src/librats/core/ip_address.h +109 -0
  64. package/native-src/src/librats/core/mpsc_queue.h +47 -0
  65. package/native-src/src/librats/core/notifier.h +74 -0
  66. package/native-src/src/librats/core/receive_buffer.cpp +219 -0
  67. package/native-src/src/librats/core/receive_buffer.h +171 -0
  68. package/native-src/src/librats/core/service_registry.h +58 -0
  69. package/native-src/src/{socket.cpp → librats/core/socket.cpp} +625 -118
  70. package/native-src/src/librats/core/socket.h +496 -0
  71. package/native-src/src/librats/core/timer_queue.h +105 -0
  72. package/native-src/src/librats/core/types.cpp +43 -0
  73. package/native-src/src/librats/core/types.h +103 -0
  74. package/native-src/src/librats/core/wakeup_pipe.h +83 -0
  75. package/native-src/src/{crypto → librats/crypto}/blake2_endian.h +21 -23
  76. package/native-src/src/{crypto → librats/crypto}/blake2b.c +34 -33
  77. package/native-src/src/{crypto → librats/crypto}/blake2b.h +7 -6
  78. package/native-src/src/{crypto → librats/crypto}/blake2s.c +55 -54
  79. package/native-src/src/{crypto → librats/crypto}/blake2s.h +13 -12
  80. package/native-src/src/{crypto → librats/crypto}/chacha.c +22 -21
  81. package/native-src/src/{crypto → librats/crypto}/chacha.h +14 -13
  82. package/native-src/src/{crypto → librats/crypto}/chachapoly.c +56 -56
  83. package/native-src/src/{crypto → librats/crypto}/chachapoly.h +24 -17
  84. package/native-src/src/{crc32.cpp → librats/crypto/crc32.cpp} +1 -1
  85. package/native-src/src/{crc32.h → librats/crypto/crc32.h} +3 -1
  86. package/native-src/src/{crypto → librats/crypto}/curve25519.c +6 -4
  87. package/native-src/src/{crypto → librats/crypto}/curve25519.h +6 -3
  88. package/native-src/src/librats/crypto/hkdf.c +266 -0
  89. package/native-src/src/{crypto → librats/crypto}/hkdf.h +19 -19
  90. package/native-src/src/{noise.cpp → librats/crypto/noise.cpp} +84 -73
  91. package/native-src/src/{noise.h → librats/crypto/noise.h} +18 -8
  92. package/native-src/src/{crypto → librats/crypto}/poly1305.c +47 -46
  93. package/native-src/src/librats/crypto/poly1305.h +37 -0
  94. package/native-src/src/{sha1.cpp → librats/crypto/sha1.cpp} +33 -1
  95. package/native-src/src/{sha1.h → librats/crypto/sha1.h} +14 -6
  96. package/native-src/src/{crypto → librats/crypto}/sha256.c +15 -14
  97. package/native-src/src/{crypto → librats/crypto}/sha256.h +8 -7
  98. package/native-src/src/{crypto → librats/crypto}/sha512.c +15 -14
  99. package/native-src/src/{crypto → librats/crypto}/sha512.h +8 -7
  100. package/native-src/src/librats/dht/announce.cpp +37 -0
  101. package/native-src/src/librats/dht/announce.h +41 -0
  102. package/native-src/src/librats/dht/bep42.cpp +109 -0
  103. package/native-src/src/librats/dht/bep42.h +48 -0
  104. package/native-src/src/librats/dht/dht.cpp +501 -0
  105. package/native-src/src/librats/dht/dht.h +119 -0
  106. package/native-src/src/librats/dht/dht_runner.cpp +103 -0
  107. package/native-src/src/librats/dht/dht_runner.h +71 -0
  108. package/native-src/src/librats/dht/dos_blocker.cpp +42 -0
  109. package/native-src/src/librats/dht/dos_blocker.h +47 -0
  110. package/native-src/src/librats/dht/find_peers.cpp +52 -0
  111. package/native-src/src/librats/dht/find_peers.h +73 -0
  112. package/native-src/src/librats/dht/id.h +167 -0
  113. package/native-src/src/{krpc.cpp → librats/dht/krpc.cpp} +32 -81
  114. package/native-src/src/{krpc.h → librats/dht/krpc.h} +19 -23
  115. package/native-src/src/librats/dht/log.h +38 -0
  116. package/native-src/src/librats/dht/node.cpp +473 -0
  117. package/native-src/src/librats/dht/node.h +164 -0
  118. package/native-src/src/librats/dht/node_entry.h +81 -0
  119. package/native-src/src/librats/dht/observer.h +72 -0
  120. package/native-src/src/librats/dht/persistence.cpp +90 -0
  121. package/native-src/src/librats/dht/persistence.h +32 -0
  122. package/native-src/src/librats/dht/routing_table.cpp +559 -0
  123. package/native-src/src/librats/dht/routing_table.h +185 -0
  124. package/native-src/src/librats/dht/rpc_manager.cpp +127 -0
  125. package/native-src/src/librats/dht/rpc_manager.h +77 -0
  126. package/native-src/src/librats/dht/storage.cpp +92 -0
  127. package/native-src/src/librats/dht/storage.h +74 -0
  128. package/native-src/src/librats/dht/transport.h +27 -0
  129. package/native-src/src/librats/dht/traversal.cpp +326 -0
  130. package/native-src/src/librats/dht/traversal.h +120 -0
  131. package/native-src/src/librats/dht/udp_transport.cpp +49 -0
  132. package/native-src/src/librats/dht/udp_transport.h +51 -0
  133. package/native-src/src/librats/mdns/log.h +22 -0
  134. package/native-src/src/{mdns.cpp → librats/mdns/mdns.cpp} +75 -40
  135. package/native-src/src/{mdns.h → librats/mdns/mdns.h} +9 -8
  136. package/native-src/src/{natpmp.cpp → librats/nat/natpmp.cpp} +12 -9
  137. package/native-src/src/{natpmp.h → librats/nat/natpmp.h} +3 -3
  138. package/native-src/src/{port_mapping.h → librats/nat/port_mapping.h} +3 -2
  139. package/native-src/src/{stun.cpp → librats/nat/stun.cpp} +4 -4
  140. package/native-src/src/{stun.h → librats/nat/stun.h} +1 -1
  141. package/native-src/src/{upnp.cpp → librats/nat/upnp.cpp} +6 -6
  142. package/native-src/src/{upnp.h → librats/nat/upnp.h} +2 -2
  143. package/native-src/src/librats/node/circuit_service.h +84 -0
  144. package/native-src/src/librats/node/config.h +110 -0
  145. package/native-src/src/librats/node/dial_service.h +54 -0
  146. package/native-src/src/librats/node/dialer.cpp +264 -0
  147. package/native-src/src/librats/node/dialer.h +188 -0
  148. package/native-src/src/librats/node/host_events.h +26 -0
  149. package/native-src/src/librats/node/identify.cpp +130 -0
  150. package/native-src/src/librats/node/identify.h +71 -0
  151. package/native-src/src/librats/node/nat_status.cpp +103 -0
  152. package/native-src/src/librats/node/nat_status.h +118 -0
  153. package/native-src/src/librats/node/node.cpp +865 -0
  154. package/native-src/src/librats/node/node.h +344 -0
  155. package/native-src/src/librats/node/node_context.h +33 -0
  156. package/native-src/src/librats/node/peer_network.h +91 -0
  157. package/native-src/src/librats/peer/peer.h +49 -0
  158. package/native-src/src/librats/peer/peer_book.cpp +181 -0
  159. package/native-src/src/librats/peer/peer_book.h +88 -0
  160. package/native-src/src/librats/peer/peer_id.cpp +72 -0
  161. package/native-src/src/librats/peer/peer_id.h +62 -0
  162. package/native-src/src/librats/peer/peer_info.h +37 -0
  163. package/native-src/src/librats/peer/peer_table.cpp +170 -0
  164. package/native-src/src/librats/peer/peer_table.h +148 -0
  165. package/native-src/src/librats/security/handshaker.h +66 -0
  166. package/native-src/src/librats/security/identity.h +43 -0
  167. package/native-src/src/librats/security/noise_security.cpp +122 -0
  168. package/native-src/src/librats/security/noise_security.h +37 -0
  169. package/native-src/src/librats/security/plaintext_security.h +106 -0
  170. package/native-src/src/librats/security/session.h +37 -0
  171. package/native-src/src/{storage.cpp → librats/storage/storage.cpp} +369 -522
  172. package/native-src/src/{storage.h → librats/storage/storage.h} +135 -299
  173. package/native-src/src/librats/subsystems/bittorrent.cpp +211 -0
  174. package/native-src/src/librats/subsystems/bittorrent.h +136 -0
  175. package/native-src/src/librats/subsystems/dht_discovery.cpp +202 -0
  176. package/native-src/src/librats/subsystems/dht_discovery.h +123 -0
  177. package/native-src/src/librats/subsystems/dht_service.h +36 -0
  178. package/native-src/src/librats/subsystems/file_transfer.cpp +972 -0
  179. package/native-src/src/librats/subsystems/file_transfer.h +367 -0
  180. package/native-src/src/librats/subsystems/hole_punch.cpp +605 -0
  181. package/native-src/src/librats/subsystems/hole_punch.h +290 -0
  182. package/native-src/src/librats/subsystems/hole_punch_service.h +38 -0
  183. package/native-src/src/librats/subsystems/mdns_discovery.cpp +66 -0
  184. package/native-src/src/librats/subsystems/mdns_discovery.h +55 -0
  185. package/native-src/src/librats/subsystems/message_json.cpp +112 -0
  186. package/native-src/src/librats/subsystems/message_json.h +88 -0
  187. package/native-src/src/librats/subsystems/peer_exchange.cpp +241 -0
  188. package/native-src/src/librats/subsystems/peer_exchange.h +136 -0
  189. package/native-src/src/librats/subsystems/ping_service.cpp +98 -0
  190. package/native-src/src/librats/subsystems/ping_service.h +66 -0
  191. package/native-src/src/librats/subsystems/port_mapping_service.cpp +192 -0
  192. package/native-src/src/librats/subsystems/port_mapping_service.h +84 -0
  193. package/native-src/src/librats/subsystems/pubsub.cpp +567 -0
  194. package/native-src/src/librats/subsystems/pubsub.h +175 -0
  195. package/native-src/src/librats/subsystems/reconnection.cpp +239 -0
  196. package/native-src/src/librats/subsystems/reconnection.h +126 -0
  197. package/native-src/src/librats/subsystems/relay.cpp +1142 -0
  198. package/native-src/src/librats/subsystems/relay.h +211 -0
  199. package/native-src/src/librats/subsystems/relay_service.h +46 -0
  200. package/native-src/src/librats/transport/connection.cpp +343 -0
  201. package/native-src/src/librats/transport/connection.h +283 -0
  202. package/native-src/src/librats/transport/link.h +96 -0
  203. package/native-src/src/librats/transport/reactor.cpp +588 -0
  204. package/native-src/src/librats/transport/reactor.h +262 -0
  205. package/native-src/src/librats/transport/reactor_pool.h +81 -0
  206. package/native-src/src/librats/transport/relay_link.cpp +208 -0
  207. package/native-src/src/librats/transport/relay_link.h +303 -0
  208. package/native-src/src/librats/transport/tcp_link.cpp +49 -0
  209. package/native-src/src/librats/transport/tcp_link.h +43 -0
  210. package/native-src/src/librats/transport/udp_mux.cpp +617 -0
  211. package/native-src/src/librats/transport/udp_mux.h +363 -0
  212. package/native-src/src/librats/transport/udp_packet.cpp +121 -0
  213. package/native-src/src/librats/transport/udp_packet.h +190 -0
  214. package/native-src/src/librats/transport/udp_stream.cpp +1194 -0
  215. package/native-src/src/librats/transport/udp_stream.h +614 -0
  216. package/native-src/src/librats/util/features.h.in +51 -0
  217. package/native-src/src/{fs.cpp → librats/util/fs.cpp} +51 -3
  218. package/native-src/src/librats/util/fs.h +136 -0
  219. package/native-src/src/librats/util/json.cpp +1002 -0
  220. package/native-src/src/librats/util/json.h +444 -0
  221. package/native-src/src/{logger.cpp → librats/util/logger.cpp} +1 -1
  222. package/native-src/src/{logger.h → librats/util/logger.h} +43 -31
  223. package/native-src/src/{network_monitor.cpp → librats/util/network_monitor.cpp} +12 -4
  224. package/native-src/src/{network_monitor.h → librats/util/network_monitor.h} +2 -1
  225. package/native-src/src/{network_utils.cpp → librats/util/network_utils.cpp} +38 -23
  226. package/native-src/src/{network_utils.h → librats/util/network_utils.h} +15 -8
  227. package/native-src/src/{os.cpp → librats/util/os.cpp} +48 -18
  228. package/native-src/src/librats/util/rats_export.h +69 -0
  229. package/native-src/src/{version.cpp → librats/util/version.cpp} +2 -2
  230. package/native-src/src/{version.h.in → librats/util/version.h.in} +1 -1
  231. package/native-src/src/librats/wire/frame.cpp +76 -0
  232. package/native-src/src/librats/wire/frame.h +111 -0
  233. package/native-src/src/librats/wire/message_router.cpp +45 -0
  234. package/native-src/src/librats/wire/message_router.h +47 -0
  235. package/package.json +5 -4
  236. package/scripts/build-librats.js +1 -0
  237. package/scripts/postinstall.js +3 -3
  238. package/scripts/prepare-package.js +4 -4
  239. package/scripts/verify-installation.js +63 -105
  240. package/src/librats_node.cpp +1067 -1323
  241. package/native-src/src/bencode.cpp +0 -485
  242. package/native-src/src/bencode.h +0 -145
  243. package/native-src/src/bittorrent.cpp +0 -14
  244. package/native-src/src/bittorrent.h +0 -74
  245. package/native-src/src/bt_bitfield.cpp +0 -372
  246. package/native-src/src/bt_bitfield.h +0 -316
  247. package/native-src/src/bt_choker.cpp +0 -228
  248. package/native-src/src/bt_choker.h +0 -147
  249. package/native-src/src/bt_client.cpp +0 -1047
  250. package/native-src/src/bt_client.h +0 -445
  251. package/native-src/src/bt_create_torrent.cpp +0 -677
  252. package/native-src/src/bt_create_torrent.h +0 -473
  253. package/native-src/src/bt_extension.cpp +0 -469
  254. package/native-src/src/bt_extension.h +0 -309
  255. package/native-src/src/bt_file_storage.cpp +0 -261
  256. package/native-src/src/bt_file_storage.h +0 -298
  257. package/native-src/src/bt_handshake.cpp +0 -134
  258. package/native-src/src/bt_handshake.h +0 -157
  259. package/native-src/src/bt_messages.cpp +0 -364
  260. package/native-src/src/bt_messages.h +0 -324
  261. package/native-src/src/bt_network.cpp +0 -1007
  262. package/native-src/src/bt_network.h +0 -417
  263. package/native-src/src/bt_peer_connection.cpp +0 -742
  264. package/native-src/src/bt_peer_connection.h +0 -592
  265. package/native-src/src/bt_piece_picker.cpp +0 -786
  266. package/native-src/src/bt_piece_picker.h +0 -473
  267. package/native-src/src/bt_resume_data.cpp +0 -410
  268. package/native-src/src/bt_resume_data.h +0 -249
  269. package/native-src/src/bt_torrent.cpp +0 -2120
  270. package/native-src/src/bt_torrent.h +0 -641
  271. package/native-src/src/bt_torrent_info.cpp +0 -659
  272. package/native-src/src/bt_torrent_info.h +0 -418
  273. package/native-src/src/bt_types.h +0 -621
  274. package/native-src/src/chained_send_buffer.cpp +0 -75
  275. package/native-src/src/chained_send_buffer.h +0 -137
  276. package/native-src/src/crypto/hkdf.c +0 -266
  277. package/native-src/src/crypto/poly1305.h +0 -36
  278. package/native-src/src/dht.cpp +0 -3311
  279. package/native-src/src/dht.h +0 -717
  280. package/native-src/src/disk_io.cpp +0 -632
  281. package/native-src/src/disk_io.h +0 -315
  282. package/native-src/src/file_transfer.cpp +0 -1415
  283. package/native-src/src/file_transfer.h +0 -286
  284. package/native-src/src/fs.h +0 -108
  285. package/native-src/src/gossipsub.cpp +0 -1139
  286. package/native-src/src/gossipsub.h +0 -403
  287. package/native-src/src/ice.cpp +0 -893
  288. package/native-src/src/ice.h +0 -559
  289. package/native-src/src/json.hpp +0 -25526
  290. package/native-src/src/librats.cpp +0 -2378
  291. package/native-src/src/librats.h +0 -2324
  292. package/native-src/src/librats_bittorrent.cpp +0 -601
  293. package/native-src/src/librats_c.cpp +0 -1557
  294. package/native-src/src/librats_c.h +0 -323
  295. package/native-src/src/librats_discovery.cpp +0 -402
  296. package/native-src/src/librats_encryption.cpp +0 -275
  297. package/native-src/src/librats_file_transfer.cpp +0 -144
  298. package/native-src/src/librats_gossipsub.cpp +0 -289
  299. package/native-src/src/librats_ice.cpp +0 -213
  300. package/native-src/src/librats_log_macros.h +0 -36
  301. package/native-src/src/librats_logging.cpp +0 -173
  302. package/native-src/src/librats_mdns.cpp +0 -166
  303. package/native-src/src/librats_persistence.cpp +0 -796
  304. package/native-src/src/librats_portmap.cpp +0 -419
  305. package/native-src/src/librats_reconnection.cpp +0 -218
  306. package/native-src/src/librats_statistic.cpp +0 -105
  307. package/native-src/src/librats_storage.cpp +0 -189
  308. package/native-src/src/rats_export.h +0 -17
  309. package/native-src/src/receive_buffer.cpp +0 -82
  310. package/native-src/src/receive_buffer.h +0 -127
  311. package/native-src/src/socket.h +0 -228
  312. package/native-src/src/threadmanager.cpp +0 -105
  313. package/native-src/src/threadmanager.h +0 -53
  314. package/native-src/src/tracker.cpp +0 -1264
  315. package/native-src/src/tracker.h +0 -319
  316. package/native-src/src/turn.cpp +0 -762
  317. package/native-src/src/turn.h +0 -460
  318. package/native-src/src/wakeup_pipe.h +0 -60
  319. /package/native-src/src/{os.h → librats/util/os.h} +0 -0
@@ -0,0 +1,352 @@
1
+ #include "librats/bittorrent/piece_picker.h"
2
+ #include "librats/bittorrent/log.h"
3
+ #include "librats/bittorrent/types.h" // kBlockSize
4
+
5
+ #include <algorithm>
6
+
7
+ namespace librats::bittorrent {
8
+
9
+ PiecePicker::PiecePicker(std::uint32_t num_pieces, std::uint32_t piece_length, std::int64_t total_size)
10
+ : num_pieces_(num_pieces)
11
+ , piece_length_(piece_length)
12
+ , total_size_(total_size)
13
+ , availability_(num_pieces, 0)
14
+ , priority_(num_pieces, PiecePriority::Normal)
15
+ , have_(num_pieces, false)
16
+ , pieces_left_(num_pieces)
17
+ , order_pos_(num_pieces, 0)
18
+ , in_order_(num_pieces, 0)
19
+ , rng_(std::random_device{}()) {
20
+ // Every piece starts wanted (Normal priority, not-have, availability 0), so it
21
+ // all begins life in the single bucket {Normal, 0}.
22
+ for (std::uint32_t p = 0; p < num_pieces_; ++p) order_insert(p);
23
+ }
24
+
25
+ // ---- geometry ----
26
+
27
+ std::uint32_t PiecePicker::piece_size(std::uint32_t piece) const noexcept {
28
+ if (piece + 1 < num_pieces_) return piece_length_;
29
+ const std::int64_t tail = total_size_ - std::int64_t(piece) * piece_length_;
30
+ if (tail <= 0) return 0;
31
+ return std::uint32_t(std::min<std::int64_t>(tail, piece_length_));
32
+ }
33
+
34
+ std::uint32_t PiecePicker::blocks_in_piece(std::uint32_t piece) const noexcept {
35
+ return (piece_size(piece) + kBlockSize - 1) / kBlockSize;
36
+ }
37
+
38
+ std::uint32_t PiecePicker::block_size(std::uint32_t piece, std::uint32_t block) const noexcept {
39
+ const std::uint32_t ps = piece_size(piece);
40
+ const std::uint32_t off = block * kBlockSize;
41
+ return (std::min)(kBlockSize, ps - off);
42
+ }
43
+
44
+ // ---- our progress ----
45
+
46
+ void PiecePicker::we_have(std::uint32_t piece) {
47
+ if (have_.get(piece)) return;
48
+ order_remove(piece); // no longer wanted — pull it out of the rarest index
49
+ have_.set(piece);
50
+ ++num_have_;
51
+ if (priority_[piece] != PiecePriority::DontDownload && pieces_left_ > 0) --pieces_left_;
52
+ downloading_.erase(piece);
53
+ // Advance the sequential cursor over any now-contiguous prefix of have pieces.
54
+ while (seq_cursor_ < num_pieces_ && have_.get(seq_cursor_)) ++seq_cursor_;
55
+ }
56
+
57
+ void PiecePicker::we_have_all() {
58
+ for (std::uint32_t p = 0; p < num_pieces_; ++p) we_have(p);
59
+ }
60
+
61
+ void PiecePicker::set_have_bitfield(const Bitfield& have) {
62
+ for (std::uint32_t p = 0; p < num_pieces_; ++p)
63
+ if (have.size() > p && have.get(p)) we_have(p);
64
+ }
65
+
66
+ // ---- priorities ----
67
+
68
+ void PiecePicker::set_piece_priority(std::uint32_t piece, PiecePriority priority) {
69
+ const PiecePriority old = priority_[piece];
70
+ if (old == priority) return;
71
+ order_remove(piece); // remove under the OLD key before the priority changes
72
+ if (!have_.get(piece)) {
73
+ const bool was_wanted = old != PiecePriority::DontDownload;
74
+ const bool now_wanted = priority != PiecePriority::DontDownload;
75
+ if (was_wanted && !now_wanted) { --pieces_left_; downloading_.erase(piece); }
76
+ else if (!was_wanted && now_wanted) ++pieces_left_;
77
+ }
78
+ priority_[piece] = priority;
79
+ order_insert(piece); // re-file under the new key (no-op if it is now unwanted)
80
+ }
81
+
82
+ // ---- availability ----
83
+
84
+ void PiecePicker::peer_has_piece(std::uint32_t piece) { avail_add(piece, +1); }
85
+ void PiecePicker::peer_lost_piece(std::uint32_t piece) { avail_add(piece, -1); }
86
+ void PiecePicker::inc_availability(const Bitfield& peer_have) {
87
+ for (std::uint32_t p = 0; p < num_pieces_; ++p)
88
+ if (peer_have.size() > p && peer_have.get(p)) avail_add(p, +1);
89
+ }
90
+ void PiecePicker::dec_availability(const Bitfield& peer_have) {
91
+ for (std::uint32_t p = 0; p < num_pieces_; ++p)
92
+ if (peer_have.size() > p && peer_have.get(p)) avail_add(p, -1);
93
+ }
94
+ // A seed has every piece. Bumping every piece's availability (O(n) plus a bucket
95
+ // move each) is wasted work: since a seed shifts *all* pieces equally, the
96
+ // rarest-first ordering is unchanged, so the bucket index (keyed on the per-peer
97
+ // count) needs no update at all. Track seeds as one counter instead — the
98
+ // effective availability of a piece is availability_[p] + seeds_ — making a seed
99
+ // joining or leaving O(1). (Mirrors libtorrent's m_seeds.)
100
+ void PiecePicker::inc_availability_all() { ++seeds_; }
101
+ void PiecePicker::dec_availability_all() { if (seeds_ > 0) --seeds_; }
102
+
103
+ // ---- block state machine ----
104
+
105
+ PiecePicker::DownloadingPiece& PiecePicker::downloading_for(std::uint32_t piece) {
106
+ auto it = downloading_.find(piece);
107
+ if (it != downloading_.end()) return it->second;
108
+ DownloadingPiece dp;
109
+ dp.blocks.resize(blocks_in_piece(piece));
110
+ return downloading_.emplace(piece, std::move(dp)).first->second;
111
+ }
112
+
113
+ void PiecePicker::mark_requested(const PieceBlock& b, const void* peer) {
114
+ DownloadingPiece& dp = downloading_for(b.piece);
115
+ Block& blk = dp.blocks[b.block];
116
+ if (blk.state == BlockState::None) {
117
+ blk.state = BlockState::Requested;
118
+ ++dp.num_requested;
119
+ }
120
+ if (std::find(blk.peers.begin(), blk.peers.end(), peer) == blk.peers.end())
121
+ blk.peers.push_back(peer);
122
+ }
123
+
124
+ std::vector<const void*> PiecePicker::mark_writing(const PieceBlock& b, const void* peer) {
125
+ std::vector<const void*> others;
126
+ auto it = downloading_.find(b.piece);
127
+ if (it == downloading_.end()) return others;
128
+ Block& blk = it->second.blocks[b.block];
129
+ if (blk.state == BlockState::Requested) {
130
+ --it->second.num_requested;
131
+ blk.state = BlockState::Writing;
132
+ ++it->second.num_writing;
133
+ // Hand back the *other* requesters (end-game) so the caller can CANCEL the
134
+ // now-redundant duplicate requests, then drop the requester set — the block
135
+ // is in flight to disk and must not be picked or re-requested again.
136
+ for (const void* p : blk.peers)
137
+ if (p != peer) others.push_back(p);
138
+ blk.peers.clear();
139
+ }
140
+ return others;
141
+ }
142
+
143
+ bool PiecePicker::mark_finished(const PieceBlock& b) {
144
+ DownloadingPiece& dp = downloading_for(b.piece);
145
+ Block& blk = dp.blocks[b.block];
146
+ if (blk.state == BlockState::Requested) --dp.num_requested;
147
+ else if (blk.state == BlockState::Writing) --dp.num_writing;
148
+ if (blk.state != BlockState::Finished) { blk.state = BlockState::Finished; ++dp.num_finished; }
149
+ blk.peers.clear();
150
+ return dp.num_finished == dp.blocks.size();
151
+ }
152
+
153
+ void PiecePicker::abort_block(const PieceBlock& b, const void* peer) {
154
+ auto it = downloading_.find(b.piece);
155
+ if (it == downloading_.end()) return;
156
+ Block& blk = it->second.blocks[b.block];
157
+ blk.peers.erase(std::remove(blk.peers.begin(), blk.peers.end(), peer), blk.peers.end());
158
+ if (blk.state == BlockState::Requested && blk.peers.empty()) {
159
+ blk.state = BlockState::None;
160
+ --it->second.num_requested;
161
+ }
162
+ }
163
+
164
+ void PiecePicker::cancel_peer(const void* peer) {
165
+ for (auto& [piece, dp] : downloading_) {
166
+ for (auto& blk : dp.blocks) {
167
+ blk.peers.erase(std::remove(blk.peers.begin(), blk.peers.end(), peer), blk.peers.end());
168
+ if (blk.state == BlockState::Requested && blk.peers.empty()) {
169
+ blk.state = BlockState::None;
170
+ --dp.num_requested;
171
+ }
172
+ }
173
+ }
174
+ }
175
+
176
+ void PiecePicker::restore_piece(std::uint32_t piece) {
177
+ downloading_.erase(piece); // drop all block progress; piece becomes fresh
178
+ }
179
+
180
+ BlockState PiecePicker::block_state(std::uint32_t piece, std::uint32_t block) const {
181
+ if (have_.get(piece)) return BlockState::Finished;
182
+ auto it = downloading_.find(piece);
183
+ if (it == downloading_.end()) return BlockState::None;
184
+ return it->second.blocks[block].state;
185
+ }
186
+
187
+ bool PiecePicker::is_interesting(const Bitfield& peer_have) const {
188
+ for (std::uint32_t p = 0; p < num_pieces_; ++p)
189
+ if (wanted(p) && peer_have.size() > p && peer_have.get(p)) return true;
190
+ return false;
191
+ }
192
+
193
+ // ---- picking ----
194
+
195
+ void PiecePicker::append_free_blocks(std::uint32_t piece, int count, const void* peer,
196
+ std::vector<PieceBlock>& out) const {
197
+ const std::uint32_t nblocks = blocks_in_piece(piece);
198
+ auto it = downloading_.find(piece);
199
+ for (std::uint32_t b = 0; b < nblocks && int(out.size()) < count; ++b) {
200
+ if (it == downloading_.end() || it->second.blocks[b].state == BlockState::None)
201
+ out.push_back(PieceBlock{piece, b});
202
+ }
203
+ }
204
+
205
+ void PiecePicker::order_insert(std::uint32_t piece) {
206
+ if (in_order_[piece] || !wanted(piece)) return;
207
+ std::vector<std::vector<std::uint32_t>>& col = buckets_[prio_idx(priority_[piece])];
208
+ const std::uint32_t av = availability_[piece];
209
+ if (col.size() <= av) col.resize(av + 1); // grow the availability dimension on demand
210
+ std::vector<std::uint32_t>& bucket = col[av];
211
+ order_pos_[piece] = std::uint32_t(bucket.size());
212
+ bucket.push_back(piece);
213
+ in_order_[piece] = 1;
214
+ }
215
+
216
+ void PiecePicker::order_remove(std::uint32_t piece) {
217
+ if (!in_order_[piece]) return;
218
+ // Bucket coords must match the ones used at insert — callers remove *before*
219
+ // mutating priority_/availability_ (see avail_add / set_piece_priority).
220
+ std::vector<std::uint32_t>& bucket = buckets_[prio_idx(priority_[piece])][availability_[piece]];
221
+ const std::uint32_t pos = order_pos_[piece];
222
+ const std::uint32_t last = bucket.back();
223
+ bucket[pos] = last; // swap the tail piece into the vacated slot...
224
+ order_pos_[last] = pos; // ...and fix up its recorded position
225
+ bucket.pop_back(); // empty buckets simply stay empty — no alloc churn
226
+ in_order_[piece] = 0;
227
+ }
228
+
229
+ void PiecePicker::avail_add(std::uint32_t piece, int delta) {
230
+ // Remove under the current key, change availability, then re-file under the new
231
+ // key — an O(log n) bucket move. (For a have/unwanted piece the index ops are
232
+ // no-ops; we still track its availability count.)
233
+ order_remove(piece);
234
+ if (delta >= 0) {
235
+ availability_[piece] += std::uint32_t(delta);
236
+ } else {
237
+ const std::uint32_t d = std::uint32_t(-delta);
238
+ availability_[piece] = availability_[piece] > d ? availability_[piece] - d : 0;
239
+ }
240
+ order_insert(piece);
241
+ }
242
+
243
+ std::vector<PieceBlock> PiecePicker::pick_blocks(const Bitfield& peer_have, int count, const void* peer) {
244
+ std::vector<PieceBlock> result;
245
+ if (count <= 0) return result;
246
+
247
+ auto peer_has = [&](std::uint32_t p) { return peer_have.size() > p && peer_have.get(p); };
248
+
249
+ // 1) Finish pieces already in progress that this peer can serve. In sequential
250
+ // mode finish the earliest partial first (streaming order); otherwise the map
251
+ // order is fine. (downloading_ is small — bounded by peers × pieces-in-flight.)
252
+ auto take_partial = [&](std::uint32_t piece) {
253
+ if (int(result.size()) >= count) return;
254
+ if (!wanted(piece) || !peer_has(piece)) return;
255
+ append_free_blocks(piece, count, peer, result);
256
+ };
257
+ if (mode_ == PickMode::Sequential) {
258
+ std::vector<std::uint32_t> partials;
259
+ partials.reserve(downloading_.size());
260
+ for (const auto& kv : downloading_) partials.push_back(kv.first);
261
+ std::sort(partials.begin(), partials.end());
262
+ for (std::uint32_t piece : partials) {
263
+ if (int(result.size()) >= count) break;
264
+ take_partial(piece);
265
+ }
266
+ } else {
267
+ for (const auto& [piece, dp] : downloading_) {
268
+ if (int(result.size()) >= count) break;
269
+ take_partial(piece);
270
+ }
271
+ }
272
+ if (int(result.size()) >= count) return result;
273
+
274
+ // 2) Start new pieces, ordered by the active strategy.
275
+ auto try_new_piece = [&](std::uint32_t p) {
276
+ if (!wanted(p) || !peer_has(p) || downloading_.count(p)) return;
277
+ append_free_blocks(p, count, peer, result);
278
+ };
279
+ switch (mode_) {
280
+ case PickMode::Sequential:
281
+ // Start at the cursor: every piece below it is already have, so there
282
+ // is nothing to pick there. This keeps the hot refill path off the
283
+ // completed prefix instead of rescanning it from 0 every time.
284
+ for (std::uint32_t p = seq_cursor_; p < num_pieces_ && int(result.size()) < count; ++p)
285
+ try_new_piece(p);
286
+ break;
287
+ case PickMode::Random: {
288
+ // Walk the rarest-first buckets — which already hold exactly the wanted
289
+ // pieces — in a shuffled order, each entered at a random offset. That is
290
+ // a random spread without scanning or allocating across all pieces (the
291
+ // bucket set is small: one per distinct (priority, availability)).
292
+ std::vector<std::vector<std::uint32_t>*> bkts;
293
+ for (auto& col : buckets_)
294
+ for (auto& bucket : col)
295
+ if (!bucket.empty()) bkts.push_back(&bucket);
296
+ std::shuffle(bkts.begin(), bkts.end(), rng_);
297
+ for (auto* bucket : bkts) {
298
+ if (int(result.size()) >= count) break;
299
+ const std::size_t n = bucket->size();
300
+ const std::size_t start = std::uniform_int_distribution<std::size_t>(0, n - 1)(rng_);
301
+ for (std::size_t k = 0; k < n && int(result.size()) < count; ++k)
302
+ try_new_piece((*bucket)[(start + k) % n]);
303
+ }
304
+ break;
305
+ }
306
+ case PickMode::RarestFirst:
307
+ // Visit buckets best-first: High priority before Normal, and within a
308
+ // priority ascending availability (rarest first). Within a bucket every
309
+ // piece is equally good, so start at a random offset and wrap around:
310
+ // this spreads concurrent peers across the rarest pieces instead of all
311
+ // converging on the same one — without any sorting here.
312
+ for (int pi = 0; pi < kNumPrio && int(result.size()) < count; ++pi) {
313
+ std::vector<std::vector<std::uint32_t>>& col = buckets_[pi];
314
+ for (std::uint32_t av = 0; av < col.size() && int(result.size()) < count; ++av) {
315
+ std::vector<std::uint32_t>& bucket = col[av];
316
+ if (bucket.empty()) continue;
317
+ const std::size_t n = bucket.size();
318
+ const std::size_t start = std::uniform_int_distribution<std::size_t>(0, n - 1)(rng_);
319
+ for (std::size_t k = 0; k < n && int(result.size()) < count; ++k)
320
+ try_new_piece(bucket[(start + k) % n]);
321
+ }
322
+ }
323
+ break;
324
+ }
325
+ if (int(result.size()) >= count) return result;
326
+
327
+ // 3) End-game. Only enter it once *every* wanted piece is already in progress
328
+ // (downloading_ covers all remaining wanted pieces) — i.e. near completion —
329
+ // so we don't start duplicating requests early in the download (PP-1). Before
330
+ // that, a peer whose blocks are all taken simply gets nothing this round.
331
+ if (downloading_.size() < pieces_left_) return result; // fresh pieces still remain
332
+
333
+ // Duplicate at most ONE already-requested block per call (not this peer's whole
334
+ // budget) so an in-progress block isn't fetched from many peers at once.
335
+ for (const auto& [piece, dp] : downloading_) {
336
+ if (!wanted(piece) || !peer_has(piece)) continue;
337
+ const std::uint32_t nblocks = blocks_in_piece(piece);
338
+ for (std::uint32_t b = 0; b < nblocks; ++b) {
339
+ const Block& blk = dp.blocks[b];
340
+ if (blk.state != BlockState::Requested) continue;
341
+ if (std::find(blk.peers.begin(), blk.peers.end(), peer) != blk.peers.end()) continue;
342
+ result.push_back(PieceBlock{piece, b});
343
+ if (!endgame_) LOG_DEBUG("bt.picker", "entering end-game (" << pieces_left_
344
+ << " piece(s) left, all in progress)");
345
+ endgame_ = true;
346
+ return result; // one busy block per pick
347
+ }
348
+ }
349
+ return result;
350
+ }
351
+
352
+ } // namespace librats::bittorrent
@@ -0,0 +1,201 @@
1
+ #pragma once
2
+
3
+ /**
4
+ * @file piece_picker.h
5
+ * @brief Decides which blocks to request next — the download strategy core.
6
+ *
7
+ * The picker tracks, for every piece: how many connected peers have it
8
+ * (availability), our download priority for it, whether we already have it, and
9
+ * — for pieces in progress — the per-block state machine
10
+ * (None → Requested → Writing → Finished). pick_blocks() turns all of that into
11
+ * a concrete list of blocks to ask a given peer for, honouring:
12
+ *
13
+ * 1. partial pieces first — finish what's already started;
14
+ * 2. the active strategy — rarest-first (default), sequential or random;
15
+ * 3. end-game — once every needed block is already in flight,
16
+ * re-request outstanding blocks from other peers.
17
+ *
18
+ * pick_blocks() is a pure query: it never mutates state. The caller commits its
19
+ * choices with mark_requested(), so the two-queue request pipeline (reserved vs
20
+ * in-flight) stays in the peer connection. Owned by one torrent on the network
21
+ * thread — not thread-safe by design.
22
+ */
23
+
24
+ #include "librats/bittorrent/bitfield.h"
25
+
26
+ #include <array>
27
+ #include <cstdint>
28
+ #include <random>
29
+ #include <unordered_map>
30
+ #include <utility>
31
+ #include <vector>
32
+
33
+ namespace librats::bittorrent {
34
+
35
+ enum class PiecePriority : std::uint8_t {
36
+ DontDownload = 0, ///< excluded from picking
37
+ Normal = 1,
38
+ High = 2,
39
+ };
40
+
41
+ enum class PickMode : std::uint8_t {
42
+ RarestFirst, ///< default — fewest-available pieces first
43
+ Sequential, ///< lowest piece index first (streaming)
44
+ Random,
45
+ };
46
+
47
+ enum class BlockState : std::uint8_t {
48
+ None, ///< not requested
49
+ Requested, ///< asked a peer, awaiting data
50
+ Writing, ///< received, being written to disk
51
+ Finished, ///< on disk
52
+ };
53
+
54
+ /// A single 16 KiB block, identified by its piece and its index within it.
55
+ struct PieceBlock {
56
+ std::uint32_t piece = 0;
57
+ std::uint32_t block = 0;
58
+ bool operator==(const PieceBlock& o) const noexcept { return piece == o.piece && block == o.block; }
59
+ bool operator!=(const PieceBlock& o) const noexcept { return !(*this == o); }
60
+ };
61
+
62
+ class PiecePicker {
63
+ public:
64
+ PiecePicker(std::uint32_t num_pieces, std::uint32_t piece_length, std::int64_t total_size);
65
+
66
+ // ---- geometry ----
67
+ std::uint32_t num_pieces() const noexcept { return num_pieces_; }
68
+ std::uint32_t piece_length() const noexcept { return piece_length_; }
69
+ std::uint32_t piece_size(std::uint32_t piece) const noexcept;
70
+ std::uint32_t blocks_in_piece(std::uint32_t piece) const noexcept;
71
+ std::uint32_t block_size(std::uint32_t piece, std::uint32_t block) const noexcept;
72
+
73
+ // ---- our progress ----
74
+ void we_have(std::uint32_t piece);
75
+ void we_have_all();
76
+ bool have_piece(std::uint32_t piece) const noexcept { return have_.get(piece); }
77
+ std::uint32_t num_have() const noexcept { return num_have_; }
78
+ bool is_finished() const noexcept { return pieces_left_ == 0; }
79
+ Bitfield have_bitfield() const { return have_; }
80
+ void set_have_bitfield(const Bitfield& have);
81
+
82
+ // ---- priorities ----
83
+ void set_piece_priority(std::uint32_t piece, PiecePriority priority);
84
+ PiecePriority piece_priority(std::uint32_t piece) const noexcept { return priority_[piece]; }
85
+
86
+ // ---- strategy ----
87
+ void set_mode(PickMode mode) noexcept { mode_ = mode; }
88
+ PickMode mode() const noexcept { return mode_; }
89
+
90
+ // ---- peer availability ----
91
+ void peer_has_piece(std::uint32_t piece);
92
+ void peer_lost_piece(std::uint32_t piece);
93
+ void inc_availability(const Bitfield& peer_have);
94
+ void dec_availability(const Bitfield& peer_have);
95
+ void inc_availability_all(); ///< a seed joined
96
+ void dec_availability_all();
97
+ /// Effective availability: per-peer count plus the number of connected seeds
98
+ /// (seeds are tracked as one counter — see inc_availability_all).
99
+ std::uint32_t availability(std::uint32_t piece) const noexcept { return availability_[piece] + seeds_; }
100
+
101
+ // ---- block state machine ----
102
+ /// Up to @p count blocks that @p peer (holding @p peer_have) can serve and we
103
+ /// still need. Pure query — commit with mark_requested().
104
+ std::vector<PieceBlock> pick_blocks(const Bitfield& peer_have, int count, const void* peer);
105
+
106
+ void mark_requested(const PieceBlock& b, const void* peer);
107
+ /// Mark a received block as being written to disk. Returns the *other* peers
108
+ /// that also had this block requested (end-game duplicates) so the caller can
109
+ /// send them CANCEL; the block's requester set is then cleared. Empty in the
110
+ /// common, non-end-game case where only the delivering peer had it.
111
+ std::vector<const void*> mark_writing(const PieceBlock& b, const void* peer);
112
+ /// Mark a block on disk. Returns true if this completed the piece (all blocks
113
+ /// finished → ready to hash).
114
+ bool mark_finished(const PieceBlock& b);
115
+ /// A request failed/was rejected: drop @p peer from the block, freeing it if no
116
+ /// other peer is still on it.
117
+ void abort_block(const PieceBlock& b, const void* peer);
118
+ /// A peer vanished: free every block it had outstanding.
119
+ void cancel_peer(const void* peer);
120
+ /// Hash check failed: throw away a piece's block progress so it is re-downloaded.
121
+ void restore_piece(std::uint32_t piece);
122
+
123
+ BlockState block_state(std::uint32_t piece, std::uint32_t block) const;
124
+ bool is_interesting(const Bitfield& peer_have) const;
125
+ /// True if @p piece is one we still want (not have, not DontDownload). O(1).
126
+ /// A peer that has this piece is therefore interesting to us — this lets the
127
+ /// interest update on a single HAVE stay O(1) instead of rescanning the whole
128
+ /// bitfield through is_interesting().
129
+ bool piece_interesting(std::uint32_t piece) const noexcept {
130
+ return piece < num_pieces_ && wanted(piece);
131
+ }
132
+ bool in_endgame() const noexcept { return endgame_; }
133
+ std::size_t num_downloading() const noexcept { return downloading_.size(); }
134
+
135
+ private:
136
+ struct Block {
137
+ BlockState state = BlockState::None;
138
+ std::vector<const void*> peers; ///< requesters (>1 only in end-game)
139
+ };
140
+ struct DownloadingPiece {
141
+ std::vector<Block> blocks;
142
+ std::uint16_t num_requested = 0;
143
+ std::uint16_t num_writing = 0;
144
+ std::uint16_t num_finished = 0;
145
+ };
146
+
147
+ bool wanted(std::uint32_t piece) const noexcept {
148
+ return priority_[piece] != PiecePriority::DontDownload && !have_.get(piece);
149
+ }
150
+ DownloadingPiece& downloading_for(std::uint32_t piece);
151
+ void append_free_blocks(std::uint32_t piece, int count, const void* peer,
152
+ std::vector<PieceBlock>& out) const;
153
+
154
+ // ---- incremental rarest-first index ----
155
+ // Every wanted & not-yet-have piece sits in a bucket addressed directly by
156
+ // (priority level, availability). A single availability or priority change is
157
+ // an O(1) bucket move (order_remove + order_insert) — no tree, no node
158
+ // allocation. pick_blocks() walks the buckets best-first (High priority first,
159
+ // then ascending availability = rarest). availability is bounded by the peer
160
+ // count (seeds live in seeds_, not here), so the inner dimension stays small.
161
+ static constexpr int kNumPrio = 2; ///< High (idx 0, picked first) and Normal (idx 1)
162
+ static int prio_idx(PiecePriority p) noexcept {
163
+ return p == PiecePriority::High ? 0 : 1; // DontDownload is never bucketed
164
+ }
165
+ void order_insert(std::uint32_t piece); ///< add piece to its bucket (if wanted & absent)
166
+ void order_remove(std::uint32_t piece); ///< pull piece out of its current bucket (if present)
167
+ void avail_add(std::uint32_t piece, int delta); ///< change availability, keeping the index sorted
168
+
169
+ std::uint32_t num_pieces_;
170
+ std::uint32_t piece_length_;
171
+ std::int64_t total_size_;
172
+
173
+ std::vector<std::uint32_t> availability_;
174
+ std::vector<PiecePriority> priority_;
175
+ Bitfield have_;
176
+ std::uint32_t num_have_ = 0;
177
+ std::uint32_t pieces_left_ = 0; ///< wanted & not-yet-have
178
+ PickMode mode_ = PickMode::RarestFirst;
179
+ bool endgame_ = false;
180
+ // Sequential cursor: every piece with index < seq_cursor_ is already `have`,
181
+ // so a sequential pick can start here instead of rescanning the completed
182
+ // prefix from 0 on every refill. Maintained only in we_have() (advances
183
+ // monotonically, O(n) total over the whole download).
184
+ std::uint32_t seq_cursor_ = 0;
185
+ // Number of connected seeds (peers that have everything). Tracked as one
186
+ // counter instead of +1 on every piece; folded into availability().
187
+ std::uint32_t seeds_ = 0;
188
+
189
+ std::unordered_map<std::uint32_t, DownloadingPiece> downloading_;
190
+
191
+ // Incremental rarest-first index: buckets_[prio_idx][availability] -> pieces.
192
+ // The inner vector grows on demand as higher availabilities appear (bounded by
193
+ // the peer count). order_pos_[p] is p's slot within its bucket (for O(1)
194
+ // swap-removal); in_order_[p] records whether p is currently bucketed at all.
195
+ std::array<std::vector<std::vector<std::uint32_t>>, kNumPrio> buckets_;
196
+ std::vector<std::uint32_t> order_pos_;
197
+ std::vector<std::uint8_t> in_order_;
198
+ mutable std::mt19937 rng_;
199
+ };
200
+
201
+ } // namespace librats::bittorrent
@@ -0,0 +1,97 @@
1
+ #include "librats/bittorrent/reactor.h"
2
+
3
+ namespace librats::bittorrent {
4
+
5
+ Reactor::Reactor() : poller_(IOPoller::create()) {
6
+ if (is_valid_socket(notifier_.fd())) poller_->add(notifier_.fd(), PollIn);
7
+ }
8
+
9
+ Reactor::~Reactor() {
10
+ stop();
11
+ }
12
+
13
+ void Reactor::start() {
14
+ if (running_.exchange(true)) return;
15
+ thread_ = std::thread([this] {
16
+ loop_thread_ = std::this_thread::get_id();
17
+ while (running_.load()) run_one(1000);
18
+ });
19
+ }
20
+
21
+ void Reactor::run() {
22
+ running_.store(true);
23
+ loop_thread_ = std::this_thread::get_id();
24
+ while (running_.load()) run_one(1000);
25
+ }
26
+
27
+ void Reactor::stop() {
28
+ if (!running_.exchange(false)) {
29
+ if (thread_.joinable()) thread_.join();
30
+ return;
31
+ }
32
+ notifier_.signal(); // break the poll wait so the loop sees running_ == false
33
+ if (thread_.joinable()) thread_.join();
34
+ }
35
+
36
+ void Reactor::post(Task task) {
37
+ tasks_.push(std::move(task));
38
+ notifier_.signal();
39
+ }
40
+
41
+ void Reactor::run_one(int timeout_ms) {
42
+ if (loop_thread_ == std::thread::id{}) loop_thread_ = std::this_thread::get_id();
43
+
44
+ const int wait_ms = timers_.next_timeout_ms(timeout_ms);
45
+ PollResult results[128];
46
+ const int n = poller_->wait(results, 128, wait_ms);
47
+
48
+ for (int i = 0; i < n; ++i) {
49
+ if (results[i].fd == notifier_.fd()) {
50
+ notifier_.drain();
51
+ continue;
52
+ }
53
+ auto it = handlers_.find(results[i].fd);
54
+ if (it == handlers_.end()) continue;
55
+ // Copy the callback: it may remove this very fd (and destroy the stored
56
+ // std::function) while running.
57
+ IoCallback cb = it->second;
58
+ cb(results[i].events);
59
+ }
60
+
61
+ run_tasks();
62
+ timers_.run_due();
63
+ }
64
+
65
+ void Reactor::run_tasks() {
66
+ tasks_.drain(task_scratch_);
67
+ for (auto& t : task_scratch_) t();
68
+ task_scratch_.clear();
69
+ }
70
+
71
+ bool Reactor::add(socket_t fd, std::uint32_t events, IoCallback cb) {
72
+ handlers_[fd] = std::move(cb);
73
+ if (!poller_->add(fd, events)) {
74
+ handlers_.erase(fd);
75
+ return false;
76
+ }
77
+ return true;
78
+ }
79
+
80
+ bool Reactor::modify(socket_t fd, std::uint32_t events) {
81
+ return poller_->modify(fd, events);
82
+ }
83
+
84
+ void Reactor::remove(socket_t fd) {
85
+ poller_->remove(fd);
86
+ handlers_.erase(fd);
87
+ }
88
+
89
+ TimerId Reactor::schedule(std::chrono::milliseconds delay, TimerCallback cb) {
90
+ return timers_.schedule(delay, std::move(cb));
91
+ }
92
+
93
+ void Reactor::cancel(TimerId id) {
94
+ timers_.cancel(id);
95
+ }
96
+
97
+ } // namespace librats::bittorrent